From ddceb34906882d9170f107b2ad738e8f7851da5e Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 2 May 2019 10:17:49 -0500
Subject: [PATCH] winebus.sys: Don't report the guide button

This breaks our xinput.
---
 dlls/winebus.sys/bus_sdl.c | 9 ++++++---
 dlls/xinput1_3/hid.c       | 5 ++---
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index f5781465cdb..47ea4b4b54c 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -149,17 +149,17 @@ static const BYTE REPORT_AXIS_TAIL[] = {
 };
 #define IDX_ABS_AXIS_COUNT 23
 
-#define CONTROLLER_NUM_BUTTONS 11
+#define CONTROLLER_NUM_BUTTONS 10
 
 static const BYTE CONTROLLER_BUTTONS[] = {
     0x05, 0x09, /* USAGE_PAGE (Button) */
     0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
-    0x29, CONTROLLER_NUM_BUTTONS, /* USAGE_MAXIMUM (Button 11) */
+    0x29, CONTROLLER_NUM_BUTTONS, /* USAGE_MAXIMUM (Button 10) */
     0x15, 0x00, /* LOGICAL_MINIMUM (0) */
     0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
     0x35, 0x00, /* LOGICAL_MINIMUM (0) */
     0x45, 0x01, /* LOGICAL_MAXIMUM (1) */
-    0x95, CONTROLLER_NUM_BUTTONS, /* REPORT_COUNT (11) */
+    0x95, CONTROLLER_NUM_BUTTONS, /* REPORT_COUNT (10) */
     0x75, 0x01, /* REPORT_SIZE (1) */
     0x81, 0x02, /* INPUT (Data,Var,Abs) */
 };
@@ -841,7 +841,10 @@ static BOOL set_mapped_report_from_event(SDL_Event *event)
                 case SDL_CONTROLLER_BUTTON_START: usage = 7; break;
                 case SDL_CONTROLLER_BUTTON_LEFTSTICK: usage = 8; break;
                 case SDL_CONTROLLER_BUTTON_RIGHTSTICK: usage = 9; break;
+
+                /* native HID does not report the guide button
                 case SDL_CONTROLLER_BUTTON_GUIDE: usage = 10; break;
+                */
 
                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
diff --git a/dlls/xinput1_3/hid.c b/dlls/xinput1_3/hid.c
index 668e5b1ee4e..3d3a495e300 100644
--- a/dlls/xinput1_3/hid.c
+++ b/dlls/xinput1_3/hid.c
@@ -124,7 +124,7 @@ static BOOL VerifyGamepad(PHIDP_PREPARSED_DATA ppd, XINPUT_CAPABILITIES *xinput_
             button_count = max(button_count, button_caps[i].NotRange.Usage);
     }
     HeapFree(GetProcessHeap(), 0, button_caps);
-    if (button_count < 11)
+    if (button_count < 10)
         WARN("Too few buttons, continuing anyway\n");
     xinput_caps->Gamepad.wButtons = 0xffff;
 
@@ -335,7 +335,7 @@ void HID_update_state(xinput_controller* device)
     CHAR *report = private->reports[(private->current_report)%2];
     CHAR *target_report = private->reports[(private->current_report+1)%2];
 
-    USAGE buttons[11];
+    USAGE buttons[10];
     ULONG button_length, hat_value;
     LONG value;
 
@@ -379,7 +379,6 @@ void HID_update_state(xinput_controller* device)
                 case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break;
                 case 9: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break;
                 case 10: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break;
-                case 11: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break;
             }
         }
 
From 32bc7e5ba2f81d14652d96e8f6e89f17a1ff408a Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 30 Apr 2019 11:18:45 -0500
Subject: [PATCH] winebus.sys: Report triggers as a single axis

This breaks our xinput (triggers can't be de-mangled).
---
 dlls/winebus.sys/bus_sdl.c | 78 ++++++++++++++++++++++++--------------
 dlls/xinput1_3/hid.c       | 21 +++++-----
 2 files changed, 59 insertions(+), 40 deletions(-)

diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index 47ea4b4b54c..ac52e7bf690 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -182,17 +182,17 @@ static const BYTE CONTROLLER_AXIS [] = {
 static const BYTE CONTROLLER_TRIGGERS [] = {
     0x05, 0x01,         /* USAGE_PAGE (Generic Desktop) */
     0x09, 0x32,         /* USAGE (Z) */
-    0x09, 0x35,         /* USAGE (RZ) */
-    0x16, 0x00, 0x00,   /* LOGICAL_MINIMUM (0) */
-    0x26, 0xff, 0x7f,   /* LOGICAL_MAXIMUM (32767) */
-    0x36, 0x00, 0x00,   /* PHYSICAL_MINIMUM (0) */
-    0x46, 0xff, 0x7f,   /* PHYSICAL_MAXIMUM (32767) */
+    0x17, 0x00, 0x00, 0x00, 0x00,   /* LOGICAL_MINIMUM (0) */
+    0x27, 0xff, 0xff, 0x00, 0x00,   /* LOGICAL_MAXIMUM (65535) */
+    0x37, 0x00, 0x00, 0x00, 0x00,   /* PHYSICAL_MINIMUM (0) */
+    0x47, 0xff, 0xff, 0x00, 0x00,   /* PHYSICAL_MAXIMUM (65535) */
     0x75, 0x10,         /* REPORT_SIZE (16) */
-    0x95, 0x02,         /* REPORT_COUNT (2) */
+    0x95, 0x01,         /* REPORT_COUNT (1) */
     0x81, 0x02,         /* INPUT (Data,Var,Abs) */
 };
 
-#define CONTROLLER_NUM_AXES 6
+#define CONTROLLER_NUM_AXES 5
+#define COMBINED_TRIGGER_INDEX 4
 
 #define CONTROLLER_NUM_HATSWITCHES 1
 
@@ -262,24 +262,24 @@ static void set_button_value(struct platform_private *ext, int index, int value)
     }
 }
 
+static unsigned short map_axis_to_hid(short v)
+{
+    return ((int)v) + 32768;
+}
+
+static short compose_trigger_value(SDL_GameController *joystick)
+{
+    /* yes, they are combined into one value and cannot be detangled */
+    return 0x8000
+        + pSDL_GameControllerGetAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT)
+        - pSDL_GameControllerGetAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
+}
+
 static void set_axis_value(struct platform_private *ext, int index, short value)
 {
     int offset;
     offset = ext->axis_start + index * sizeof(WORD);
-
-    switch (index)
-    {
-    case SDL_CONTROLLER_AXIS_LEFTX:
-    case SDL_CONTROLLER_AXIS_LEFTY:
-    case SDL_CONTROLLER_AXIS_RIGHTX:
-    case SDL_CONTROLLER_AXIS_RIGHTY:
-        *((WORD*)&ext->report_buffer[offset]) = LE_WORD(value) + 32768;
-        break;
-    case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
-    case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
-        *((WORD*)&ext->report_buffer[offset]) = LE_WORD(value);
-        break;
-    }
+    *((WORD*)&ext->report_buffer[offset]) = LE_WORD(value);
 }
 
 static void set_ball_value(struct platform_private *ext, int index, int value1, int value2)
@@ -507,7 +507,7 @@ static BOOL build_report_descriptor(struct platform_private *ext)
 
     /* Initialize axis in the report */
     for (i = 0; i < axis_count; i++)
-        set_axis_value(ext, i, pSDL_JoystickGetAxis(ext->sdl_joystick, i));
+        set_axis_value(ext, i, map_axis_to_hid(pSDL_JoystickGetAxis(ext->sdl_joystick, i)));
     for (i = 0; i < hat_count; i++)
         set_hat_value(ext, i, pSDL_JoystickGetHat(ext->sdl_joystick, i));
 
@@ -544,7 +544,7 @@ static SHORT compose_dpad_value(SDL_GameController *joystick)
 static BOOL build_mapped_report_descriptor(struct platform_private *ext)
 {
     BYTE *report_ptr;
-    INT i, descript_size;
+    INT descript_size;
 
     static const int BUTTON_BIT_COUNT = CONTROLLER_NUM_BUTTONS + CONTROLLER_NUM_HATSWITCHES * 4;
 
@@ -604,8 +604,19 @@ static BOOL build_mapped_report_descriptor(struct platform_private *ext)
     }
 
     /* Initialize axis in the report */
-    for (i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++)
-        set_axis_value(ext, i, pSDL_GameControllerGetAxis(ext->sdl_controller, i));
+    set_axis_value(ext, SDL_CONTROLLER_AXIS_LEFTX,
+            map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_LEFTX)));
+
+    set_axis_value(ext, SDL_CONTROLLER_AXIS_LEFTY,
+            map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_LEFTY)));
+
+    set_axis_value(ext, SDL_CONTROLLER_AXIS_RIGHTX,
+            map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_RIGHTX)));
+
+    set_axis_value(ext, SDL_CONTROLLER_AXIS_RIGHTY,
+            map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_RIGHTY)));
+
+    set_axis_value(ext, COMBINED_TRIGGER_INDEX, compose_trigger_value(ext->sdl_controller));
 
     set_hat_value(ext, 0, compose_dpad_value(ext->sdl_controller));
 
@@ -780,7 +791,7 @@ static BOOL set_report_from_event(SDL_Event *event)
 
             if (ie->axis < 6)
             {
-                set_axis_value(private, ie->axis, ie->value);
+                set_axis_value(private, ie->axis, map_axis_to_hid(ie->value));
                 process_hid_report(device, private->report_buffer, private->buffer_length);
             }
             break;
@@ -868,8 +879,19 @@ static BOOL set_mapped_report_from_event(SDL_Event *event)
         case SDL_CONTROLLERAXISMOTION:
         {
             SDL_ControllerAxisEvent *ie = &event->caxis;
-
-            set_axis_value(private, ie->axis, ie->value);
+            switch (ie->axis)
+            {
+                case SDL_CONTROLLER_AXIS_LEFTX:
+                case SDL_CONTROLLER_AXIS_LEFTY:
+                case SDL_CONTROLLER_AXIS_RIGHTX:
+                case SDL_CONTROLLER_AXIS_RIGHTY:
+                    set_axis_value(private, ie->axis, map_axis_to_hid(ie->value));
+                    break;
+                case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
+                case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
+                    set_axis_value(private, COMBINED_TRIGGER_INDEX, compose_trigger_value(private->sdl_controller));
+                    break;
+            }
             process_hid_report(device, private->report_buffer, private->buffer_length);
             break;
         }
diff --git a/dlls/xinput1_3/hid.c b/dlls/xinput1_3/hid.c
index 3d3a495e300..285a1f5d1e1 100644
--- a/dlls/xinput1_3/hid.c
+++ b/dlls/xinput1_3/hid.c
@@ -78,7 +78,7 @@ struct hid_platform_private {
     BYTE current_report;
     CHAR *reports[2];
 
-    struct axis_info lx, ly, ltrigger, rx, ry, rtrigger;
+    struct axis_info lx, ly, triggers, rx, ry;
 };
 
 static DWORD last_check = 0;
@@ -91,10 +91,9 @@ static void MarkUsage(struct hid_platform_private *private, WORD usage, LONG min
     {
         case HID_USAGE_GENERIC_X: private->lx = info; break;
         case HID_USAGE_GENERIC_Y: private->ly = info; break;
-        case HID_USAGE_GENERIC_Z: private->ltrigger = info; break;
+        case HID_USAGE_GENERIC_Z: private->triggers = info; break;
         case HID_USAGE_GENERIC_RX: private->rx = info; break;
         case HID_USAGE_GENERIC_RY: private->ry = info; break;
-        case HID_USAGE_GENERIC_RZ: private->rtrigger = info; break;
     }
 }
 
@@ -146,14 +145,13 @@ static BOOL VerifyGamepad(PHIDP_PREPARSED_DATA ppd, XINPUT_CAPABILITIES *xinput_
     }
     HeapFree(GetProcessHeap(), 0, value_caps);
 
-    if (private->ltrigger.bits)
+    if (private->triggers.bits)
+    {
         xinput_caps->Gamepad.bLeftTrigger = (1u << (sizeof(xinput_caps->Gamepad.bLeftTrigger) + 1)) - 1;
-    else
-        WARN("Missing axis LeftTrigger\n");
-    if (private->rtrigger.bits)
         xinput_caps->Gamepad.bRightTrigger = (1u << (sizeof(xinput_caps->Gamepad.bRightTrigger) + 1)) - 1;
+    }
     else
-        WARN("Missing axis RightTrigger\n");
+        WARN("Missing Trigger axes\n");
     if (private->lx.bits)
         xinput_caps->Gamepad.sThumbLX = (1u << (sizeof(xinput_caps->Gamepad.sThumbLX) + 1)) - 1;
     else
@@ -434,13 +434,10 @@ void HID_update_state(xinput_controller* device)
                                         private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
             device->state.Gamepad.sThumbRY = -scale_short(value, &private->ry) - 1;
 
-        if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value,
-                                        private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-            device->state.Gamepad.bRightTrigger = scale_byte(value, &private->rtrigger);
-
         if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, &value,
                                         private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
             device->state.Gamepad.bLeftTrigger = scale_byte(value, &private->ltrigger);
+            device->state.Gamepad.bRightTrigger = scale_byte(value, &private->triggers);
     }
 
     memcpy(state, &device->state, sizeof(*state));
From 60641dd66b50d5b12e32463597319b9e1aaf60c2 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 30 Apr 2019 11:20:26 -0500
Subject: [PATCH] winebus.sys: Report axes in correct order

---
 dlls/winebus.sys/bus_sdl.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index ac52e7bf690..d02572da59a 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -138,6 +138,13 @@ static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *de
     return (struct platform_private *)get_platform_private(device);
 }
 
+static const int controller_axis_map[SDL_CONTROLLER_AXIS_MAX] = {
+    /* SDL_CONTROLLER_AXIS_LEFTX -> */ 1,
+    /* SDL_CONTROLLER_AXIS_LEFTY -> */ 0,
+    /* SDL_CONTROLLER_AXIS_RIGHTX -> */ 3,
+    /* SDL_CONTROLLER_AXIS_RIGHTY -> */ 2,
+};
+
 static const BYTE REPORT_AXIS_TAIL[] = {
     0x17, 0x00, 0x00, 0x00, 0x00,   /* LOGICAL_MINIMUM (0) */
     0x27, 0xff, 0xff, 0x00, 0x00,   /* LOGICAL_MAXIMUM (65535) */
@@ -166,10 +173,10 @@ static const BYTE CONTROLLER_BUTTONS[] = {
 
 static const BYTE CONTROLLER_AXIS [] = {
     0x05, 0x01,         /* USAGE_PAGE (Generic Desktop) */
-    0x09, 0x30,         /* USAGE (X) */
     0x09, 0x31,         /* USAGE (Y) */
-    0x09, 0x33,         /* USAGE (RX) */
+    0x09, 0x30,         /* USAGE (X) */
     0x09, 0x34,         /* USAGE (RY) */
+    0x09, 0x33,         /* USAGE (RX) */
     0x17, 0x00, 0x00, 0x00, 0x00,   /* LOGICAL_MINIMUM (0) */
     0x27, 0xff, 0xff, 0x00, 0x00,   /* LOGICAL_MAXIMUM (65535) */
     0x37, 0x00, 0x00, 0x00, 0x00,   /* PHYSICAL_MINIMUM (0) */
@@ -604,16 +611,16 @@ static BOOL build_mapped_report_descriptor(struct platform_private *ext)
     }
 
     /* Initialize axis in the report */
-    set_axis_value(ext, SDL_CONTROLLER_AXIS_LEFTX,
+    set_axis_value(ext, controller_axis_map[SDL_CONTROLLER_AXIS_LEFTX],
             map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_LEFTX)));
 
-    set_axis_value(ext, SDL_CONTROLLER_AXIS_LEFTY,
+    set_axis_value(ext, controller_axis_map[SDL_CONTROLLER_AXIS_LEFTY],
             map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_LEFTY)));
 
-    set_axis_value(ext, SDL_CONTROLLER_AXIS_RIGHTX,
+    set_axis_value(ext, controller_axis_map[SDL_CONTROLLER_AXIS_RIGHTX],
             map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_RIGHTX)));
 
-    set_axis_value(ext, SDL_CONTROLLER_AXIS_RIGHTY,
+    set_axis_value(ext, controller_axis_map[SDL_CONTROLLER_AXIS_RIGHTY],
             map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_RIGHTY)));
 
     set_axis_value(ext, COMBINED_TRIGGER_INDEX, compose_trigger_value(ext->sdl_controller));
@@ -885,7 +892,7 @@ static BOOL set_mapped_report_from_event(SDL_Event *event)
                 case SDL_CONTROLLER_AXIS_LEFTY:
                 case SDL_CONTROLLER_AXIS_RIGHTX:
                 case SDL_CONTROLLER_AXIS_RIGHTY:
-                    set_axis_value(private, ie->axis, map_axis_to_hid(ie->value));
+                    set_axis_value(private, controller_axis_map[ie->axis], map_axis_to_hid(ie->value));
                     break;
                 case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
                 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
From 9b0229fad7fe96452a89f5b6e37764c9c3ee444c Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 2 May 2019 08:16:37 -0500
Subject: [PATCH] HACK: create a duplicate xinput-only device for each SDL
 controller

---
 dlls/hidclass.sys/device.c   |  4 ++-
 dlls/hidclass.sys/hid.h      |  2 +-
 dlls/hidclass.sys/pnp.c      |  2 +-
 dlls/winebus.sys/bus.h       |  2 +-
 dlls/winebus.sys/bus_iohid.c |  2 +-
 dlls/winebus.sys/bus_sdl.c   | 53 +++++++++++++++++++++++++++++-------
 dlls/winebus.sys/bus_udev.c  |  4 +--
 dlls/winebus.sys/main.c      |  7 +++--
 dlls/xinput1_3/hid.c         |  1 +
 9 files changed, 58 insertions(+), 19 deletions(-)

diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c
index 547c3554108..0481db0447a 100644
--- a/dlls/hidclass.sys/device.c
+++ b/dlls/hidclass.sys/device.c
@@ -76,7 +76,7 @@ NTSTATUS HID_CreateDevice(DEVICE_OBJECT *native_device, HID_MINIDRIVER_REGISTRAT
     return STATUS_SUCCESS;
 }
 
-NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device)
+NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device, BOOL xinput_hack)
 {
     SP_DEVINFO_DATA Data;
     UNICODE_STRING nameW;
@@ -86,6 +86,8 @@ NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device)
     BASE_DEVICE_EXTENSION *ext;
 
     HidD_GetHidGuid(&hidGuid);
+    if(xinput_hack)
+        hidGuid.Data4[7]++; /* HACK: use different GUID so only xinput will find this device */
     ext = device->DeviceExtension;
 
     RtlInitUnicodeString( &nameW, ext->device_name);
diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h
index 769b7155ee6..3b34de321a5 100644
--- a/dlls/hidclass.sys/hid.h
+++ b/dlls/hidclass.sys/hid.h
@@ -94,7 +94,7 @@ minidriver* find_minidriver(DRIVER_OBJECT* driver) DECLSPEC_HIDDEN;
 
 /* Internal device functions */
 NTSTATUS HID_CreateDevice(DEVICE_OBJECT *native_device, HID_MINIDRIVER_REGISTRATION *driver, DEVICE_OBJECT **device) DECLSPEC_HIDDEN;
-NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device) DECLSPEC_HIDDEN;
+NTSTATUS HID_LinkDevice(DEVICE_OBJECT *device, BOOL xinput_hack) DECLSPEC_HIDDEN;
 void HID_DeleteDevice(DEVICE_OBJECT *device) DECLSPEC_HIDDEN;
 void HID_StartDeviceThread(DEVICE_OBJECT *device) DECLSPEC_HIDDEN;
 
diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c
index 08aae159f22..1a26307f05f 100644
--- a/dlls/hidclass.sys/pnp.c
+++ b/dlls/hidclass.sys/pnp.c
@@ -199,7 +199,7 @@ NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, DEVICE_OBJECT *PDO)
 
     sprintfW(ext->device_id, device_deviceid_fmtW, device_enumeratorW, ext->information.VendorID, ext->information.ProductID);
 
-    HID_LinkDevice(device);
+    HID_LinkDevice(device, attr.Reserved[0]/*xinput_hack*/);
 
     ext->poll_interval = DEFAULT_POLL_INTERVAL;
 
diff --git a/dlls/winebus.sys/bus.h b/dlls/winebus.sys/bus.h
index 16e9bf7d540..673cb2953ce 100644
--- a/dlls/winebus.sys/bus.h
+++ b/dlls/winebus.sys/bus.h
@@ -44,7 +44,7 @@ void *get_platform_private(DEVICE_OBJECT *device) DECLSPEC_HIDDEN;
 /* HID Plug and Play Bus */
 DEVICE_OBJECT *bus_create_hid_device(const WCHAR *busidW, WORD vid, WORD pid,
                                      WORD input, DWORD version, DWORD uid, const WCHAR *serialW, BOOL is_gamepad,
-                                     const platform_vtbl *vtbl, DWORD platform_data_size) DECLSPEC_HIDDEN;
+                                     const platform_vtbl *vtbl, DWORD platform_data_size, BOOL xinput_hack) DECLSPEC_HIDDEN;
 DEVICE_OBJECT *bus_find_hid_device(const platform_vtbl *vtbl, void *platform_dev) DECLSPEC_HIDDEN;
 void bus_remove_hid_device(DEVICE_OBJECT *device) DECLSPEC_HIDDEN;
 NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
diff --git a/dlls/winebus.sys/bus_iohid.c b/dlls/winebus.sys/bus_iohid.c
index 95077ac0be2..bd8986f2d4c 100644
--- a/dlls/winebus.sys/bus_iohid.c
+++ b/dlls/winebus.sys/bus_iohid.c
@@ -344,7 +344,7 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void *
 
     device = bus_create_hid_device(busidW, vid, pid, input,
             version, uid, str ? serial_string : NULL, is_gamepad,
-            &iohid_vtbl, sizeof(struct platform_private));
+            &iohid_vtbl, sizeof(struct platform_private), FALSE);
     if (!device)
         ERR("Failed to create device\n");
     else
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index d02572da59a..4608f249c4b 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -71,6 +71,8 @@ DEFINE_GUID(GUID_DEVCLASS_SDL, 0x463d60b5,0x802b,0x4bb2,0x8f,0xdb,0x7d,0xa9,0xb9
 
 static void *sdl_handle = NULL;
 
+#define XINPUT_HACK_ID_BIT 0x80000000
+
 #ifdef SONAME_LIBSDL2
 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
 MAKE_FUNCPTR(SDL_GetError);
@@ -131,6 +134,8 @@ struct platform_private
 
     SDL_Haptic *sdl_haptic;
     int haptic_effect_id;
+
+    BOOL xinput_hack;
 };
 
 static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *device)
@@ -548,7 +553,7 @@ static SHORT compose_dpad_value(SDL_GameController *joystick)
     return SDL_HAT_CENTERED;
 }
 
-static BOOL build_mapped_report_descriptor(struct platform_private *ext)
+static BOOL build_mapped_report_descriptor(struct platform_private *ext, BOOL xinput_hack)
 {
     BYTE *report_ptr;
     INT descript_size;
@@ -920,7 +923,7 @@ static void try_remove_device(SDL_JoystickID index)
     bus_remove_hid_device(device);
 }
 
-static void try_add_device(unsigned int index)
+static void try_add_device(unsigned int index, BOOL xinput_hack)
 {
     DWORD vid = 0, pid = 0, version = 0;
     DEVICE_OBJECT *device = NULL;
@@ -942,7 +945,16 @@ static void try_add_device(SDL_JoystickID index)
     if (map_controllers && pSDL_IsGameController(index))
         controller = pSDL_GameControllerOpen(index);
 
+    if (xinput_hack && (!map_controllers || !controller))
+    {
+        /* xinput hack only applies to mapped controllers */
+        pSDL_JoystickClose(joystick);
+        return;
+    }
+
     id = pSDL_JoystickInstanceID(joystick);
+    if(xinput_hack)
+        id |= XINPUT_HACK_ID_BIT;
 
     if (pSDL_JoystickGetProductVersion != NULL) {
         vid = pSDL_JoystickGetVendor(joystick);
@@ -962,15 +974,15 @@ static void try_add_device(SDL_JoystickID index)
 
     if (controller)
     {
-        TRACE("Found sdl game controller %i (vid %04x, pid %04x, version %u, serial %s)\n",
-              id, vid, pid, version, debugstr_w(serial));
+        TRACE("Found sdl game controller 0x%x (vid %04x, pid %04x, version %u, serial %s, xinput_hack: %u)\n",
+              id, vid, pid, version, debugstr_w(serial), xinput_hack);
         is_xbox_gamepad = TRUE;
     }
     else
     {
         int button_count, axis_count;
 
-        TRACE("Found sdl device %i (vid %04x, pid %04x, version %u, serial %s)\n",
+        TRACE("Found sdl device 0x%x (vid %04x, pid %04x, version %u, serial %s)\n",
               id, vid, pid, version, debugstr_w(serial));
 
         axis_count = pSDL_JoystickNumAxes(joystick);
@@ -978,7 +990,7 @@ static void try_add_device(SDL_JoystickID index)
         input = 0;
 
     device = bus_create_hid_device(sdl_busidW, vid, pid, input, version, index,
-            serial, is_xbox_gamepad, &sdl_vtbl, sizeof(struct platform_private));
+            serial, is_xbox_gamepad, &sdl_vtbl, sizeof(struct platform_private), xinput_hack);
 
     if (device)
     {
@@ -988,7 +1000,7 @@ static void try_add_device(SDL_JoystickID index)
         private->sdl_controller = controller;
         private->id = id;
         if (controller)
-            rc = build_mapped_report_descriptor(private);
+            rc = build_mapped_report_descriptor(private, xinput_hack);
         else
             rc = build_report_descriptor(private);
         if (!rc)
@@ -1011,13 +1023,33 @@ static void process_device_event(SDL_Event *event)
     TRACE_(hid_report)("Received action %x\n", event->type);
 
     if (event->type == SDL_JOYDEVICEADDED)
-        try_add_device(((SDL_JoyDeviceEvent*)event)->which);
+    {
+        try_add_device(((SDL_JoyDeviceEvent*)event)->which, FALSE);
+        try_add_device(((SDL_JoyDeviceEvent*)event)->which, TRUE);
+    }
     else if (event->type == SDL_JOYDEVICEREMOVED)
+    {
         try_remove_device(((SDL_JoyDeviceEvent*)event)->which);
+        try_remove_device(((SDL_JoyDeviceEvent*)event)->which | XINPUT_HACK_ID_BIT);
+    }
     else if (event->type >= SDL_JOYAXISMOTION && event->type <= SDL_JOYBUTTONUP)
+    {
+        SDL_Event xinput_hack_event = *event;
+
         set_report_from_event(event);
+
+        ((SDL_JoyAxisEvent*)&xinput_hack_event)->which |= XINPUT_HACK_ID_BIT;
+        set_report_from_event(&xinput_hack_event);
+    }
     else if (event->type >= SDL_CONTROLLERAXISMOTION && event->type <= SDL_CONTROLLERBUTTONUP)
+    {
+        SDL_Event xinput_hack_event = *event;
+
         set_mapped_report_from_event(event);
+
+        ((SDL_JoyAxisEvent*)&xinput_hack_event)->which |= XINPUT_HACK_ID_BIT;
+        set_mapped_report_from_event(&xinput_hack_event);
+    }
 }
 
 static DWORD CALLBACK deviceloop_thread(void *args)
diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index c1b93d2bfd7..72b17179e64 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -1203,13 +1203,13 @@ static void try_add_device(struct udev_device *dev)
     if (strcmp(subsystem, "hidraw") == 0)
     {
         device = bus_create_hid_device(hidraw_busidW, vid, pid, input, version, 0, serial, is_gamepad,
-                                       &hidraw_vtbl, sizeof(struct platform_private));
+                                       &hidraw_vtbl, sizeof(struct platform_private), FALSE);
     }
 #ifdef HAS_PROPER_INPUT_HEADER
     else if (strcmp(subsystem, "input") == 0)
     {
         device = bus_create_hid_device(lnxev_busidW, vid, pid, input, version, 0, serial, is_gamepad,
-                                       &lnxev_vtbl, sizeof(struct wine_input_private));
+                                       &lnxev_vtbl, sizeof(struct wine_input_private), FALSE);
     }
 #endif
 
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c
index 299cec5034d..678de08b238 100644
--- a/dlls/winebus.sys/main.c
+++ b/dlls/winebus.sys/main.c
@@ -73,7 +73,7 @@ struct device_extension
 
     WORD vid, pid;
     DWORD uid, version, index;
-    BOOL is_gamepad;
+    BOOL is_gamepad, xinput_hack;
     WCHAR *serial;
     const WCHAR *busid;  /* Expected to be a static constant */
 
@@ -198,7 +198,8 @@ static WCHAR *get_compatible_ids(DEVICE_OBJECT *device)
 
 DEVICE_OBJECT *bus_create_hid_device(const WCHAR *busidW, WORD vid, WORD pid,
                                      WORD input, DWORD version, DWORD uid, const WCHAR *serialW, BOOL is_gamepad,
-                                     const platform_vtbl *vtbl, DWORD platform_data_size)
+                                     const platform_vtbl *vtbl, DWORD platform_data_size,
+                                     BOOL xinput_hack)
 {
     static const WCHAR device_name_fmtW[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
     struct device_extension *ext;
@@ -238,6 +239,7 @@ DEVICE_OBJECT *bus_create_hid_device(DRIVER_OBJECT *driver, const WCHAR *busidW,
     ext->version            = version;
     ext->index              = get_vidpid_index(vid, pid);
     ext->is_gamepad         = is_gamepad;
+    ext->xinput_hack = xinput_hack;
     ext->serial             = strdupW(serialW);
     ext->busid              = busidW;
     ext->vtbl               = vtbl;
@@ -519,6 +521,7 @@ NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp)
             attr->VendorID = ext->vid;
             attr->ProductID = ext->pid;
             attr->VersionNumber = ext->version;
+            attr->Reserved[0] = ext->xinput_hack;
 
             irp->IoStatus.u.Status = status = STATUS_SUCCESS;
             irp->IoStatus.Information = sizeof(*attr);
diff --git a/dlls/xinput1_3/hid.c b/dlls/xinput1_3/hid.c
index 285a1f5d1e1..139bd4fa6ea 100644
--- a/dlls/xinput1_3/hid.c
+++ b/dlls/xinput1_3/hid.c
@@ -216,6 +216,7 @@ void HID_find_gamepads(xinput_controller *devices)
     last_check = idx;
 
     HidD_GetHidGuid(&hid_guid);
+    hid_guid.Data4[7]++; /* HACK: look up the xinput-specific devices */
 
     EnterCriticalSection(&hid_xinput_crit);
 
From 001f701b97ff374b7121bc665eeab5e9abfac172 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 2 May 2019 08:50:13 -0500
Subject: [PATCH] HACK: treat xinput-only controllers specially

---
 dlls/winebus.sys/bus_sdl.c | 77 +++++++++++++++++++++++++++++++-------
 dlls/xinput1_3/hid.c       | 13 +++++--
 2 files changed, 72 insertions(+), 18 deletions(-)

diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index 4608f249c4b..3c8b8acec52 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -162,6 +162,7 @@ static const BYTE REPORT_AXIS_TAIL[] = {
 #define IDX_ABS_AXIS_COUNT 23
 
 #define CONTROLLER_NUM_BUTTONS 10
+#define CONTROLLER_NUM_BUTTONS_XINPUT_HACK 11
 
 static const BYTE CONTROLLER_BUTTONS[] = {
     0x05, 0x09, /* USAGE_PAGE (Button) */
@@ -176,6 +177,19 @@ static const BYTE CONTROLLER_BUTTONS[] = {
     0x81, 0x02, /* INPUT (Data,Var,Abs) */
 };
 
+static const BYTE CONTROLLER_BUTTONS_XINPUT_HACK[] = {
+    0x05, 0x09, /* USAGE_PAGE (Button) */
+    0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
+    0x29, CONTROLLER_NUM_BUTTONS_XINPUT_HACK, /* USAGE_MAXIMUM (Button 11) */
+    0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+    0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
+    0x35, 0x00, /* LOGICAL_MINIMUM (0) */
+    0x45, 0x01, /* LOGICAL_MAXIMUM (1) */
+    0x95, CONTROLLER_NUM_BUTTONS_XINPUT_HACK, /* REPORT_COUNT (11) */
+    0x75, 0x01, /* REPORT_SIZE (1) */
+    0x81, 0x02, /* INPUT (Data,Var,Abs) */
+};
+
 static const BYTE CONTROLLER_AXIS [] = {
     0x05, 0x01,         /* USAGE_PAGE (Generic Desktop) */
     0x09, 0x31,         /* USAGE (Y) */
@@ -279,12 +293,24 @@ static unsigned short map_axis_to_hid(short v)
     return ((int)v) + 32768;
 }
 
-static short compose_trigger_value(SDL_GameController *joystick)
+static unsigned char map_trigger_to_byte(short v)
+{
+    return ((int)v) * 255 / 32767;
+}
+
+static short compose_trigger_value(struct platform_private *private)
 {
+    if(private->xinput_hack)
+    {
+        /* The range for triggers is [0,32767], so we have to map it to a byte. */
+        return map_trigger_to_byte(pSDL_GameControllerGetAxis(private->sdl_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT)) << 8 |
+                map_trigger_to_byte(pSDL_GameControllerGetAxis(private->sdl_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT));
+    }
+
     /* yes, they are combined into one value and cannot be detangled */
     return 0x8000
-        + pSDL_GameControllerGetAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT)
-        - pSDL_GameControllerGetAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
+        + pSDL_GameControllerGetAxis(private->sdl_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT)
+        - pSDL_GameControllerGetAxis(private->sdl_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
 }
 
 static void set_axis_value(struct platform_private *ext, int index, short value)
@@ -558,7 +584,7 @@ static BOOL build_mapped_report_descriptor(struct platform_private *ext, BOOL xi
     BYTE *report_ptr;
     INT descript_size;
 
-    static const int BUTTON_BIT_COUNT = CONTROLLER_NUM_BUTTONS + CONTROLLER_NUM_HATSWITCHES * 4;
+    int BUTTON_BIT_COUNT;
 
     descript_size = sizeof(REPORT_HEADER) + sizeof(REPORT_TAIL);
     descript_size += sizeof(CONTROLLER_AXIS);
@@ -566,13 +592,25 @@ static BOOL build_mapped_report_descriptor(struct platform_private *ext, BOOL xi
     descript_size += sizeof(CONTROLLER_BUTTONS);
     descript_size += sizeof(REPORT_HATSWITCH);
     descript_size += sizeof(REPORT_PADDING);
-    if (BUTTON_BIT_COUNT % 8 != 0)
-        descript_size += sizeof(REPORT_PADDING);
     descript_size += test_haptic(ext);
 
     ext->axis_start = 0;
     ext->button_start = CONTROLLER_NUM_AXES * sizeof(WORD);
-    ext->hat_bit_offs = CONTROLLER_NUM_BUTTONS;
+
+    if(ext->xinput_hack)
+    {
+        ext->hat_bit_offs = CONTROLLER_NUM_BUTTONS_XINPUT_HACK;
+        BUTTON_BIT_COUNT = CONTROLLER_NUM_BUTTONS_XINPUT_HACK + CONTROLLER_NUM_HATSWITCHES * 4;
+    }
+    else
+    {
+        ext->hat_bit_offs = CONTROLLER_NUM_BUTTONS;
+        BUTTON_BIT_COUNT = CONTROLLER_NUM_BUTTONS + CONTROLLER_NUM_HATSWITCHES * 4;
+    }
+
+
+    if (BUTTON_BIT_COUNT % 8 != 0)
+        descript_size += sizeof(REPORT_PADDING);
 
     ext->buffer_length = (BUTTON_BIT_COUNT + 7) / 8
         + CONTROLLER_NUM_AXES * sizeof(WORD)
@@ -597,8 +635,16 @@ static BOOL build_mapped_report_descriptor(struct platform_private *ext, BOOL xi
     report_ptr += sizeof(CONTROLLER_AXIS);
     memcpy(report_ptr, CONTROLLER_TRIGGERS, sizeof(CONTROLLER_TRIGGERS));
     report_ptr += sizeof(CONTROLLER_TRIGGERS);
-    memcpy(report_ptr, CONTROLLER_BUTTONS, sizeof(CONTROLLER_BUTTONS));
-    report_ptr += sizeof(CONTROLLER_BUTTONS);
+    if(ext->xinput_hack)
+    {
+        memcpy(report_ptr, CONTROLLER_BUTTONS_XINPUT_HACK, sizeof(CONTROLLER_BUTTONS_XINPUT_HACK));
+        report_ptr += sizeof(CONTROLLER_BUTTONS_XINPUT_HACK);
+    }
+    else
+    {
+        memcpy(report_ptr, CONTROLLER_BUTTONS, sizeof(CONTROLLER_BUTTONS));
+        report_ptr += sizeof(CONTROLLER_BUTTONS);
+    }
     report_ptr = add_hatswitch(report_ptr, 1);
     if (BUTTON_BIT_COUNT % 8 != 0)
         report_ptr = add_padding_block(report_ptr, 8 - (BUTTON_BIT_COUNT % 8));/* unused bits between hatswitch and following constant */
@@ -628,7 +674,7 @@ static BOOL build_mapped_report_descriptor(struct platform_private *ext, BOOL xi
     set_axis_value(ext, controller_axis_map[SDL_CONTROLLER_AXIS_RIGHTY],
             map_axis_to_hid(pSDL_GameControllerGetAxis(ext->sdl_controller, SDL_CONTROLLER_AXIS_RIGHTY)));
 
-    set_axis_value(ext, COMBINED_TRIGGER_INDEX, compose_trigger_value(ext->sdl_controller));
+    set_axis_value(ext, COMBINED_TRIGGER_INDEX, compose_trigger_value(ext));
 
     set_hat_value(ext, 0, compose_dpad_value(ext->sdl_controller));
 
@@ -863,9 +909,11 @@ static BOOL set_mapped_report_from_event(SDL_Event *event)
                 case SDL_CONTROLLER_BUTTON_LEFTSTICK: usage = 8; break;
                 case SDL_CONTROLLER_BUTTON_RIGHTSTICK: usage = 9; break;
 
-                /* native HID does not report the guide button
-                case SDL_CONTROLLER_BUTTON_GUIDE: usage = 10; break;
-                */
+                case SDL_CONTROLLER_BUTTON_GUIDE:
+                    /* native HID does not report the guide button */
+                    if(private->xinput_hack)
+                        usage = 10;
+                    break;
 
                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
@@ -899,7 +947,7 @@ static BOOL set_mapped_report_from_event(SDL_Event *event)
                     break;
                 case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
                 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
-                    set_axis_value(private, COMBINED_TRIGGER_INDEX, compose_trigger_value(private->sdl_controller));
+                    set_axis_value(private, COMBINED_TRIGGER_INDEX, compose_trigger_value(private));
                     break;
             }
             process_hid_report(device, private->report_buffer, private->buffer_length);
@@ -999,6 +1047,7 @@ static void try_add_device(SDL_JoystickID index, BOOL xinput_hack)
         private->sdl_joystick = joystick;
         private->sdl_controller = controller;
         private->id = id;
+        private->xinput_hack = xinput_hack;
         if (controller)
             rc = build_mapped_report_descriptor(private, xinput_hack);
         else
diff --git a/dlls/xinput1_3/hid.c b/dlls/xinput1_3/hid.c
index 139bd4fa6ea..9636e46b3b1 100644
--- a/dlls/xinput1_3/hid.c
+++ b/dlls/xinput1_3/hid.c
@@ -123,7 +123,7 @@ static BOOL VerifyGamepad(PHIDP_PREPARSED_DATA ppd, XINPUT_CAPABILITIES *xinput_
             button_count = max(button_count, button_caps[i].NotRange.Usage);
     }
     HeapFree(GetProcessHeap(), 0, button_caps);
-    if (button_count < 10)
+    if (button_count < 11)
         WARN("Too few buttons, continuing anyway\n");
     xinput_caps->Gamepad.wButtons = 0xffff;
 
@@ -334,7 +334,7 @@ void HID_update_state(xinput_controller* device)
     CHAR *report = private->reports[(private->current_report)%2];
     CHAR *target_report = private->reports[(private->current_report+1)%2];
 
-    USAGE buttons[10];
+    USAGE buttons[11];
     ULONG button_length, hat_value;
     LONG value;
 
@@ -378,6 +378,7 @@ void HID_update_state(xinput_controller* device)
                 case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break;
                 case 9: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break;
                 case 10: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break;
+                case 11: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break;
             }
         }
 
@@ -434,10 +434,14 @@ void HID_update_state(xinput_controller* device)
                                         private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
             device->state.Gamepad.sThumbRY = -scale_short(value, &private->ry) - 1;
 
+       /* Wine-specific hack: Windows HID mangles trigger values irretrievably, so
+        * we instead encode them in a different format in winebus. We use that
+        * format here. We should be using WineBus to talk directly to the
+        * controller's USB device so they can be correctly mangled in HID. */
         if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, &value,
                                         private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-            device->state.Gamepad.bLeftTrigger = scale_byte(value, &private->ltrigger);
-            device->state.Gamepad.bRightTrigger = scale_byte(value, &private->triggers);
+            device->state.Gamepad.bLeftTrigger = (value >> 8) & 0xFF;//scale_byte(value, &private->ltrigger);
+            device->state.Gamepad.bRightTrigger = value & 0xFF;//scale_byte(value, &private->rtrigger);
     }
 
     memcpy(state, &device->state, sizeof(*state));
From 9ea2c9c69bdcb970c6828b5375ca0796b96f862a Mon Sep 17 00:00:00 2001
From: Alexey Prokhin <alexey@prokhin.ru>
Date: Wed, 15 May 2019 00:04:58 +0300
Subject: [PATCH] dinput: return fake DIPROP_GUIDANDPATH property for SDL
 devices

Treaks some games such as AC Unity, AC Rouge, Far Cry 5 into exclusively using xinput for gamepads.
---
 dlls/dinput/joystick_sdl.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index cd26e1b2df7..50a09ac1d3a 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -70,6 +70,7 @@ struct SDLDev {
     WORD vendor_id;
     WORD product_id;
     CHAR *name;
+    BOOL is_xbox_gamepad;
 
     BOOL has_ff, is_joystick;
     int autocenter;
@@ -180,6 +181,11 @@ static void find_sdldevs(void)
                 type == SDL_JOYSTICK_TYPE_WHEEL ||
                 type == SDL_JOYSTICK_TYPE_FLIGHT_STICK ||
                 type == SDL_JOYSTICK_TYPE_THROTTLE;
+
+            if (SDL_IsGameController(i))
+                sdldev.is_xbox_gamepad = TRUE;
+            else
+                sdldev.is_xbox_gamepad  = SDL_JoystickNumAxes(device) == 6 && SDL_JoystickNumButtons(device) >= 14;
         }
 
         if (!have_sdldevs)
@@ -648,6 +654,26 @@ static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF
             break;
         }
 
+        case (DWORD_PTR) DIPROP_GUIDANDPATH:
+        {
+            static const WCHAR formatW[] =  {'\\','\\','?','\\','H','I','D','#','V','I','D','_','%','0','4', 'x','&',
+                                             'P','I','D','_','%','0','4','x','&', '%','s','_','%','i',0};
+            static const WCHAR imW[] = {'I','M',0};
+            static const WCHAR igW[] = {'I','G',0};
+
+            LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
+
+            if (!This->sdldev->product_id || !This->sdldev->vendor_id)
+                return DIERR_UNSUPPORTED;
+
+            pd->guidClass = This->generic.base.guid;
+            sprintfW(pd->wszPath, formatW, This->sdldev->vendor_id, This->sdldev->product_id,
+                     This->sdldev->is_xbox_gamepad ? igW : imW, This->sdldev->id);
+
+            TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
+            break;
+        }
+
     default:
         return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
     }
 
From 117e6df888d18c14c12b7fa59b2c076f1230a24d Mon Sep 17 00:00:00 2001
From: Tk-Glitch <ti3nou@gmail.com>
Date: Fri, 5 Jul 2019 19:39:19 +0200
Subject: Fix too few arguments to the bus_create_hid_device function added with 9c6ea019358eadcf86159872e2890ffc94960965


diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c
index 89ea65bba6..711e333f4c 100644
--- a/dlls/winebus.sys/main.c
+++ b/dlls/winebus.sys/main.c
@@ -838,7 +838,7 @@ static void mouse_device_create(void)
 {
     static const WCHAR busidW[] = {'W','I','N','E','M','O','U','S','E',0};
 
-    mouse_obj = bus_create_hid_device(busidW, 0, 0, -1, 0, 0, busidW, FALSE, &mouse_vtbl, 0);
+    mouse_obj = bus_create_hid_device(busidW, 0, 0, -1, 0, 0, busidW, FALSE, &mouse_vtbl, 0, 0);
     IoInvalidateDeviceRelations(bus_pdo, BusRelations);
 }
 
From 45fda2263b4dac746e356fd603d9f1207d7b8f6a Mon Sep 17 00:00:00 2001
From: Brendan Shanks <bshanks@codeweavers.com>
Date: Wed, 19 Jun 2019 14:40:42 -0700
Subject: [PATCH] dinput: Use the "PIDVID" GUID for the device product GUID

Windows uses {PID_VID-0000-0000-0000-504944564944} ("PIDVID") for
guidProduct.

This GUID is not officially documented, but DiRT Rally 2.0 compares
against the entire GUID to detect specific devices (and seems to disable
force feedback for unknown devices).
SDL and GLFW both look for "PIDVID" in Data4 before extracting the
PID/VID from Data1.

Signed-off-by: Brendan Shanks <bshanks@codeweavers.com>
---
 dlls/dinput/joystick_sdl.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 50a09ac1d3a..2872788f5c7 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -214,7 +218,7 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
     lpddi->dwSize       = dwSize;
     lpddi->guidInstance = DInput_Wine_SDL_Joystick_GUID;
     lpddi->guidInstance.Data3 = id;
-    lpddi->guidProduct = DInput_Wine_SDL_Joystick_GUID;
+    lpddi->guidProduct = DInput_PIDVID_Product_GUID;
     lpddi->guidProduct.Data1 = MAKELONG(sdldevs[id].vendor_id, sdldevs[id].product_id);
     lpddi->guidFFDriver = GUID_NULL;
 
@@ -389,7 +393,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
 
     newDevice->generic.guidInstance = DInput_Wine_SDL_Joystick_GUID;
     newDevice->generic.guidInstance.Data3 = index;
-    newDevice->generic.guidProduct = DInput_Wine_SDL_Joystick_GUID;
+    newDevice->generic.guidProduct = DInput_PIDVID_Product_GUID;
     newDevice->generic.guidProduct.Data1 = MAKELONG(sdldevs[index].vendor_id, sdldevs[index].product_id);
     newDevice->generic.joy_polldev = poll_sdl_device_state;
 
From 205c389287a6aa74bbf56a7a4a3a768b8830d1d5 Mon Sep 17 00:00:00 2001
From: Alexey Prokhin <alexey@prokhin.ru>
Date: Wed, 26 Jun 2019 22:36:59 +0300
Subject: [PATCH] dinput: Fix DIPROP_GUIDANDPATH property in SDL backend

---
 dlls/dinput/joystick_sdl.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 2872788f5c7..88fbca05da4 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -45,6 +45,7 @@
 #include "winbase.h"
 #include "winerror.h"
 #include "winreg.h"
+#include "devguid.h"
 #include "dinput.h"
 
 #include "dinput_private.h"
@@ -660,19 +661,19 @@ static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF
 
         case (DWORD_PTR) DIPROP_GUIDANDPATH:
         {
-            static const WCHAR formatW[] =  {'\\','\\','?','\\','H','I','D','#','V','I','D','_','%','0','4', 'x','&',
-                                             'P','I','D','_','%','0','4','x','&', '%','s','_','%','i',0};
-            static const WCHAR imW[] = {'I','M',0};
-            static const WCHAR igW[] = {'I','G',0};
+            static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
+                                            'p','i','d','_','%','0','4','x','&','%','s','_','%','i',0};
+            static const WCHAR miW[] = {'m','i',0};
+            static const WCHAR igW[] = {'i','g',0};
 
             LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
 
             if (!This->sdldev->product_id || !This->sdldev->vendor_id)
                 return DIERR_UNSUPPORTED;
 
-            pd->guidClass = This->generic.base.guid;
+            pd->guidClass = GUID_DEVCLASS_HIDCLASS;
             sprintfW(pd->wszPath, formatW, This->sdldev->vendor_id, This->sdldev->product_id,
-                     This->sdldev->is_xbox_gamepad ? igW : imW, This->sdldev->id);
+                     This->sdldev->is_xbox_gamepad ? igW : miW, This->sdldev->id);
 
             TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
             break;

From 073d099bbfa972d7980b7f287f308a34a4de4331 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Bernon?= <rbernon@codeweavers.com>
Date: Tue, 30 Jul 2019 15:00:02 +0200
Subject: [PATCH] winebus: Add xinput-specific device interface first

This way, when a game receives the HID WM_DEVICECHANGE notification,
the xinput-interface is already there and XInput does not return a
ERROR_DEVICE_NOT_CONNECTED error on first call.
---
 dlls/winebus.sys/bus_sdl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index 95a096bad89..1fa31b8cd9d 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -1101,8 +1101,8 @@ static void process_device_event(SDL_Event *event)
 
     if (event->type == SDL_JOYDEVICEADDED)
     {
-        try_add_device(((SDL_JoyDeviceEvent*)event)->which, FALSE);
         try_add_device(((SDL_JoyDeviceEvent*)event)->which, TRUE);
+        try_add_device(((SDL_JoyDeviceEvent*)event)->which, FALSE);
     }
     else if (event->type == SDL_JOYDEVICEREMOVED)
     {
From 06405e0ae3eeb4f3b6fcf33e0ac8aee678534d9b Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 6 Aug 2019 13:27:25 -0500
Subject: [PATCH] winebus.sys: Disable linuxevent API

We either go through SDL or hidraw directly.
---
 dlls/winebus.sys/bus_udev.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index a807ae7371a..6594e8a3618 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -47,7 +47,7 @@
 # include <linux/input.h>
 # undef SW_MAX
 # if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
-#  define HAS_PROPER_INPUT_HEADER
+//#  define HAS_PROPER_INPUT_HEADER
 # endif
 # ifndef SYN_DROPPED
 #  define SYN_DROPPED 3
From a15601217fe2f35337420463754522573384c893 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 19 Aug 2019 14:01:55 -0500
Subject: [PATCH] winebus.sys: Try to open devices with hidraw first

---
 dlls/winebus.sys/bus.h      |  2 ++
 dlls/winebus.sys/bus_sdl.c  | 31 +++++++++++++++++++------------
 dlls/winebus.sys/bus_udev.c | 22 ++++++++++++++++++++++
 dlls/winebus.sys/main.c     |  8 ++++----
 4 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/dlls/winebus.sys/bus.h b/dlls/winebus.sys/bus.h
index f886ff8d4ac..ff6d4f42d78 100644
--- a/dlls/winebus.sys/bus.h
+++ b/dlls/winebus.sys/bus.h
@@ -53,4 +53,6 @@ DEVICE_OBJECT* bus_enumerate_hid_devices(const platform_vtbl *vtbl, enum_func fu
 BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN;
 BOOL is_steam_controller(WORD vid, WORD pid) DECLSPEC_HIDDEN;
 
+BOOL is_already_opened_by_hidraw(DWORD vid, DWORD pid) DECLSPEC_HIDDEN;
+
 extern HANDLE driver_key DECLSPEC_HIDDEN;
 extern DEVICE_OBJECT *bus_pdo DECLSPEC_HIDDEN;
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index 1fa31b8cd9d..7423aa57fe4 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -1011,6 +1011,25 @@ static void try_add_device(unsigned int index, BOOL xinput_hack)
         return;
     }
 
+    if (pSDL_JoystickGetProductVersion != NULL) {
+        vid = pSDL_JoystickGetVendor(joystick);
+        pid = pSDL_JoystickGetProduct(joystick);
+        version = pSDL_JoystickGetProductVersion(joystick);
+    }
+    else
+    {
+        vid = 0x01;
+        pid = pSDL_JoystickInstanceID(joystick) + 1;
+        version = 0;
+    }
+
+    if(is_already_opened_by_hidraw(vid, pid))
+    {
+        /* we use SDL only for controllers which hidraw couldn't open */
+        TRACE("device %04x/%04x already opened by hidraw, skipping\n", vid, pid);
+        return;
+    }
+
     if (map_controllers && pSDL_IsGameController(index))
         controller = pSDL_GameControllerOpen(index);
 
@@ -1028,18 +1047,6 @@ static void try_add_device(unsigned int index, BOOL xinput_hack)
         index |= XINPUT_HACK_ID_BIT;
     }
 
-    if (pSDL_JoystickGetProductVersion != NULL) {
-        vid = pSDL_JoystickGetVendor(joystick);
-        pid = pSDL_JoystickGetProduct(joystick);
-        version = pSDL_JoystickGetProductVersion(joystick);
-    }
-    else
-    {
-        vid = 0x01;
-        pid = pSDL_JoystickInstanceID(joystick) + 1;
-        version = 0;
-    }
-
     guid = pSDL_JoystickGetGUID(joystick);
     pSDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
     MultiByteToWideChar(CP_ACP, 0, guid_str, -1, serial, sizeof(guid_str));
diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index 6594e8a3618..b702d75e2ca 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -100,6 +100,10 @@ static const WCHAR lnxev_busidW[] = {'L','N','X','E','V',0};
 DEFINE_GUID(GUID_DEVCLASS_HIDRAW, 0x3def44ad,0x242e,0x46e5,0x82,0x6d,0x70,0x72,0x13,0xf3,0xaa,0x81);
 DEFINE_GUID(GUID_DEVCLASS_LINUXEVENT, 0x1b932c0d,0xfea7,0x42cd,0x8e,0xaa,0x0e,0x48,0x79,0xb6,0x9e,0xaa);
 
+struct vidpid {
+    WORD vid, pid;
+};
+
 struct platform_private
 {
     struct udev_device *udev_device;
@@ -107,6 +111,8 @@ struct platform_private
 
     HANDLE report_thread;
     int control_pipe[2];
+
+    struct vidpid vidpid;
 };
 
 static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *device)
@@ -1137,6 +1143,20 @@ static DWORD a_to_bcd(const char *s)
     return r;
 }
 
+static int check_for_vidpid(DEVICE_OBJECT *device, void* context)
+{
+    struct vidpid *vidpid = context;
+    struct platform_private *dev = impl_from_DEVICE_OBJECT(device);
+    return !(dev->vidpid.vid == vidpid->vid &&
+        dev->vidpid.pid == vidpid->pid);
+}
+
+BOOL is_already_opened_by_hidraw(DWORD vid, DWORD pid)
+{
+    struct vidpid vidpid = {vid, pid};
+    return bus_enumerate_hid_devices(&hidraw_vtbl, check_for_vidpid, &vidpid) != NULL;
+}
+
 static void try_add_device(struct udev_device *dev)
 {
     DWORD vid = 0, pid = 0, version = 0;
@@ -1254,6 +1274,8 @@ static void try_add_device(struct udev_device *dev)
         struct platform_private *private = impl_from_DEVICE_OBJECT(device);
         private->udev_device = udev_device_ref(dev);
         private->device_fd = fd;
+        private->vidpid.vid = vid;
+        private->vidpid.pid = pid;
 #ifdef HAS_PROPER_INPUT_HEADER
         if (strcmp(subsystem, "input") == 0)
             if (!build_report_descriptor((struct wine_input_private*)private, dev))
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c
index dd55655572c..dd767a59d19 100644
--- a/dlls/winebus.sys/main.c
+++ b/dlls/winebus.sys/main.c
@@ -487,17 +487,18 @@ static NTSTATUS fdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp)
         break;
     case IRP_MN_START_DEVICE:
         mouse_device_create();
+        udev_driver_init();
+        iohid_driver_init();
 
         if (check_bus_option(&SDL_enabled, 1))
         {
+            sdl_driver_init();
             if (sdl_driver_init() == STATUS_SUCCESS)
             {
                 irp->IoStatus.u.Status = STATUS_SUCCESS;
                 break;
             }
         }
-        udev_driver_init();
-        iohid_driver_init();
         irp->IoStatus.u.Status = STATUS_SUCCESS;
         break;
     case IRP_MN_SURPRISE_REMOVAL:
From e176616c84e5abb9dfd6373c683e8e5be853215c Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 6 Aug 2019 13:37:38 -0500
Subject: [PATCH] winebus.sys: Don't use hidraw for xbox controllers

Xbox controllers don't present real HID devices.
---
 dlls/winebus.sys/bus_udev.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index b702d75e2ca..2a825a833ff 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -1239,7 +1239,11 @@ static void try_add_device(struct udev_device *dev)
 #endif
 
     if (is_xbox_gamepad(vid, pid))
-        is_gamepad = TRUE;
+    {
+        /* SDL handles xbox (and steam) controllers */
+        close(fd);
+        return;
+    }
 #ifdef HAS_PROPER_INPUT_HEADER
     else
     {
From 4f57e01fcaf7d31c6e085fdbd91250b47d83633b Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Wed, 14 Aug 2019 08:47:53 -0500
Subject: [PATCH] winebus: Don't report hidraw devices which are being used as
 virtual controllers

---
 dlls/winebus.sys/bus_udev.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index 2a825a833ff..be468d12319 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -18,6 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#define _GNU_SOURCE
 #include "config.h"
 #include <errno.h>
 #include <fcntl.h>
@@ -1157,6 +1158,19 @@ BOOL is_already_opened_by_hidraw(DWORD vid, DWORD pid)
     return bus_enumerate_hid_devices(&hidraw_vtbl, check_for_vidpid, &vidpid) != NULL;
 }
 
+static BOOL is_in_sdl_blacklist(DWORD vid, DWORD pid)
+{
+    char needle[16];
+    const char *blacklist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES");
+
+    if (!blacklist)
+        return FALSE;
+
+    sprintf(needle, "0x%04x/0x%04x", vid, pid);
+
+    return strcasestr(blacklist, needle) != NULL;
+}
+
 static void try_add_device(struct udev_device *dev)
 {
     DWORD vid = 0, pid = 0, version = 0;
@@ -1238,6 +1252,13 @@ static void try_add_device(struct udev_device *dev)
         WARN("Could not get device to query VID, PID, Version and Serial\n");
 #endif
 
+    if (is_in_sdl_blacklist(vid, pid))
+    {
+        /* this device is being used as a virtual Steam controller */
+        close(fd);
+        return;
+    }
+
     if (is_xbox_gamepad(vid, pid))
     {
         /* SDL handles xbox (and steam) controllers */
From e7e9fb91a2dd72c048aecdcf6650ca3f8ff33d99 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Wed, 14 Aug 2019 09:15:21 -0500
Subject: [PATCH] winebus.sys: Don't use hidraw for Steam controllers

---
 dlls/winebus.sys/bus.h      |  1 +
 dlls/winebus.sys/bus_udev.c |  4 +++-
 dlls/winebus.sys/main.c     | 24 ++++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/dlls/winebus.sys/bus.h b/dlls/winebus.sys/bus.h
index ff6d4f42d78..01dcb86da64 100644
--- a/dlls/winebus.sys/bus.h
+++ b/dlls/winebus.sys/bus.h
@@ -52,6 +52,7 @@ DEVICE_OBJECT* bus_enumerate_hid_devices(const platform_vtbl *vtbl, enum_func fu
 /* General Bus Functions */
 DWORD check_bus_option(const UNICODE_STRING *option, DWORD default_value) DECLSPEC_HIDDEN;
 BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN;
+BOOL is_steam_controller(WORD vid, WORD pid) DECLSPEC_HIDDEN;
 
 BOOL is_already_opened_by_hidraw(DWORD vid, DWORD pid) DECLSPEC_HIDDEN;
 
diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index be468d12319..161b1d5e2a8 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -1252,9 +1252,10 @@ static void try_add_device(struct udev_device *dev)
         WARN("Could not get device to query VID, PID, Version and Serial\n");
 #endif
 
-    if (is_in_sdl_blacklist(vid, pid))
+    if (is_steam_controller(vid, pid) || is_in_sdl_blacklist(vid, pid))
     {
         /* this device is being used as a virtual Steam controller */
+        TRACE("hidraw %s: ignoring device %04x/%04x with virtual Steam controller\n", debugstr_a(devnode), vid, pid);
         close(fd);
         return;
     }
@@ -1262,6 +1263,7 @@ static void try_add_device(struct udev_device *dev)
     if (is_xbox_gamepad(vid, pid))
     {
         /* SDL handles xbox (and steam) controllers */
+        TRACE("hidraw %s: ignoring xinput device %04x/%04x\n", debugstr_a(devnode), vid, pid);
         close(fd);
         return;
     }
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c
index dd767a59d19..9db40cd7be4 100644
--- a/dlls/winebus.sys/main.c
+++ b/dlls/winebus.sys/main.c
@@ -82,6 +82,18 @@ static const struct product_desc XBOX_CONTROLLERS[] = {
     {VID_MICROSOFT, 0x0719, NULL, xbox360_product_string, NULL}, /* Xbox 360 Wireless Adapter */
 };
 
+#define VID_VALVE 0x28de
+
+static const struct product_desc STEAM_CONTROLLERS[] = {
+    {VID_VALVE, 0x1101, NULL, NULL, NULL}, /* Valve Legacy Steam Controller */
+    {VID_VALVE, 0x1102, NULL, NULL, NULL}, /* Valve wired Steam Controller */
+    {VID_VALVE, 0x1105, NULL, NULL, NULL}, /* Valve Bluetooth Steam Controller */
+    {VID_VALVE, 0x1106, NULL, NULL, NULL}, /* Valve Bluetooth Steam Controller */
+    {VID_VALVE, 0x1142, NULL, NULL, NULL}, /* Valve wireless Steam Controller */
+    {VID_VALVE, 0x1201, NULL, NULL, NULL}, /* Valve wired Steam Controller */
+    {VID_VALVE, 0x1202, NULL, NULL, NULL}, /* Valve Bluetooth Steam Controller */
+};
+
 static DRIVER_OBJECT *driver_obj;
 
 HANDLE driver_key;
@@ -827,6 +839,18 @@ BOOL is_xbox_gamepad(WORD vid, WORD pid)
     return FALSE;
 }
 
+BOOL is_steam_controller(WORD vid, WORD pid)
+{
+    if (vid == VID_VALVE)
+    {
+        int i;
+        for (i = 0; i < ARRAY_SIZE(STEAM_CONTROLLERS); i++)
+            if (pid == STEAM_CONTROLLERS[i].pid) return TRUE;
+    }
+
+    return FALSE;
+}
+
 static void WINAPI driver_unload(DRIVER_OBJECT *driver)
 {
     udev_driver_unload();
From 183f90fe21134d65ef1332a4205e8f6ede65da89 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 22 Aug 2019 09:59:27 -0500
Subject: [PATCH] winebus.drv: Also respect the SDL device whitelist

---
 dlls/winebus.sys/bus_udev.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index 161b1d5e2a8..5439513b048 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -1162,6 +1162,14 @@ static BOOL is_in_sdl_blacklist(DWORD vid, DWORD pid)
 {
     char needle[16];
     const char *blacklist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES");
+    const char *whitelist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT");
+
+    if (whitelist)
+    {
+        sprintf(needle, "0x%04x/0x%04x", vid, pid);
+
+        return strcasestr(whitelist, needle) == NULL;
+    }
 
     if (!blacklist)
         return FALSE;
From d2c13155abbc9d97880ff580c44b894d919807c7 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 16 Jul 2019 08:59:24 -0500
Subject: [PATCH] dinput: Search setupapi to find xinput devices

---
 dlls/dinput/Makefile.in    |  2 +-
 dlls/dinput/joystick.c     | 73 ++++++++++++++++++++++++++------------
 dlls/dinput/joystick_sdl.c |  5 +--
 dlls/dinput8/Makefile.in   |  2 +-
 4 files changed, 53 insertions(+), 29 deletions(-)

diff --git a/dlls/dinput/Makefile.in b/dlls/dinput/Makefile.in
index a3e38816636..91aca91c25c 100644
--- a/dlls/dinput/Makefile.in
+++ b/dlls/dinput/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = dinput.dll
 IMPORTLIB = dinput
-IMPORTS   = dinput dxguid uuid comctl32 ole32 user32 advapi32
+IMPORTS   = dinput dxguid uuid comctl32 ole32 user32 advapi32 setupapi hid
 EXTRADEFS = -DDIRECTINPUT_VERSION=0x0700
 EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS) $(SDL2_LIBS)
 EXTRAINCL = $(SDL2_CFLAGS)
diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c
index a5308b6cc48..b89a83500fe 100644
--- a/dlls/dinput/joystick.c
+++ b/dlls/dinput/joystick.c
@@ -31,27 +31,11 @@
 #include "joystick_private.h"
 #include "wine/debug.h"
 #include "winreg.h"
+#include "setupapi.h"
+#include "ddk/hidsdi.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 
-#define VID_MICROSOFT 0x045e
-
-static const WORD PID_XBOX_CONTROLLERS[] =  {
-    0x0202, /* Xbox Controller */
-    0x0285, /* Xbox Controller S */
-    0x0289, /* Xbox Controller S */
-    0x028e, /* Xbox360 Controller */
-    0x028f, /* Xbox360 Wireless Controller */
-    0x02d1, /* Xbox One Controller */
-    0x02dd, /* Xbox One Controller (Covert Forces/Firmware 2015) */
-    0x02e0, /* Xbox One X Controller */
-    0x02e3, /* Xbox One Elite Controller */
-    0x02e6, /* Wireless XBox Controller Dongle */
-    0x02ea, /* Xbox One S Controller */
-    0x02fd, /* Xbox One S Controller (Firmware 2017) */
-    0x0719, /* Xbox 360 Wireless Adapter */
-};
-
 /* Windows uses this GUID for guidProduct on non-keyboard/mouse devices.
  * Data1 contains the device VID (low word) and PID (high word).
  * Data4 ends with the ASCII bytes "PIDVID".
@@ -301,15 +285,58 @@ BOOL device_disabled_registry(const char* name)
 
 BOOL is_xinput_device(const DIDEVCAPS *devcaps, WORD vid, WORD pid)
 {
-    int i;
+    HDEVINFO device_info_set;
+    GUID hid_guid;
+    SP_DEVICE_INTERFACE_DATA interface_data;
+    SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
+    DWORD idx;
+    BOOL ret = FALSE;
+    char pathA[MAX_PATH];
+
+    HidD_GetHidGuid(&hid_guid);
+    hid_guid.Data4[7]++; /* HACK: look up the xinput-specific devices */
+
+    device_info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
+
+    data = HeapAlloc(GetProcessHeap(), 0 , sizeof(*data) + MAX_PATH * sizeof(WCHAR));
+    data->cbSize = sizeof(*data);
 
-    if (vid == VID_MICROSOFT)
+    ZeroMemory(&interface_data, sizeof(interface_data));
+    interface_data.cbSize = sizeof(interface_data);
+
+    idx = 0;
+    while (!ret && SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, idx++,
+           &interface_data))
     {
-        for (i = 0; i < ARRAY_SIZE(PID_XBOX_CONTROLLERS); i++)
-            if (pid == PID_XBOX_CONTROLLERS[i]) return TRUE;
+        const char *vid_s, *pid_s;
+        DWORD di_vid = 0, di_pid = 0;
+        static const WCHAR ig[] = {'I','G','_',0};
+
+        if (!SetupDiGetDeviceInterfaceDetailW(device_info_set,
+                &interface_data, data, sizeof(*data) + MAX_PATH * sizeof(WCHAR), NULL, NULL))
+            continue;
+
+        if (!strstrW(data->DevicePath, ig))
+            continue;
+
+        WideCharToMultiByte(CP_ACP, 0, data->DevicePath, -1,
+                pathA, sizeof(pathA), NULL, NULL);
+
+        vid_s = strstr(pathA, "VID_");
+        if (vid_s)
+            sscanf(vid_s, "VID_%4X", &di_vid);
+
+        pid_s = strstr(pathA, "PID_");
+        if (pid_s)
+            sscanf(pid_s, "PID_%4X", &di_pid);
+
+        ret = vid == di_vid && pid == di_pid;
     }
 
-    return (devcaps->dwAxes == 6 && devcaps->dwButtons >= 14);
+    HeapFree(GetProcessHeap(), 0, data);
+    SetupDiDestroyDeviceInfoList(device_info_set);
+
+    return ret;
 }
 
 /******************************************************************************
diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 88fbca05da4..5426fec6d3b 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -187,10 +187,7 @@ static void find_sdldevs(void)
                 type == SDL_JOYSTICK_TYPE_FLIGHT_STICK ||
                 type == SDL_JOYSTICK_TYPE_THROTTLE;
 
-            if (SDL_IsGameController(i))
-                sdldev.is_xbox_gamepad = TRUE;
-            else
-                sdldev.is_xbox_gamepad  = SDL_JoystickNumAxes(device) == 6 && SDL_JoystickNumButtons(device) >= 14;
+            sdldev.is_xbox_gamepad = is_xinput_device(NULL, sdldev.vendor_id, sdldev.product_id);
         }
 
         if (!have_sdldevs)
diff --git a/dlls/dinput8/Makefile.in b/dlls/dinput8/Makefile.in
index 9735fb55465..4e4ba0c064e 100644
--- a/dlls/dinput8/Makefile.in
+++ b/dlls/dinput8/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = dinput8.dll
 IMPORTLIB = dinput8
-IMPORTS   = dinput8 dxguid uuid comctl32 ole32 user32 advapi32
+IMPORTS   = dinput8 dxguid uuid comctl32 ole32 user32 advapi32 setupapi hid
 EXTRADEFS = -DDIRECTINPUT_VERSION=0x0800
 EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS) $(SDL2_LIBS)
 EXTRAINCL = $(SDL2_CFLAGS)
From f398d24efec7927a6b152b71f91438ccca97d436 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 6 Aug 2019 08:42:14 -0500
Subject: [PATCH] dinput: Only enumerate SDL devices

---
 dlls/dinput/dinput_main.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c
index 1a58155db3e..7efee27d95e 100644
--- a/dlls/dinput/dinput_main.c
+++ b/dlls/dinput/dinput_main.c
@@ -91,9 +91,6 @@ static const struct dinput_device *dinput_devices[] =
     &mouse_device,
     &keyboard_device,
     &joystick_sdl_device,
-    &joystick_linuxinput_device,
-    &joystick_linux_device,
-    &joystick_osx_device
 };
 
 HINSTANCE DINPUT_instance;
From 959fa5f8ddd8cde1bf1376eeb4efeb68f25939f2 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 5 Aug 2019 10:55:26 -0500
Subject: [PATCH] dinput: Add a mapping function for specific device types

---
 dlls/dinput/joystick_sdl.c | 215 ++++++++++++++++++++++++++-----------
 1 file changed, 150 insertions(+), 65 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 5426fec6d3b..4233b25dccf 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -65,6 +65,17 @@ HRESULT sdl_create_effect(SDL_Haptic *haptic, REFGUID rguid, struct list *parent
 HRESULT sdl_input_get_info_A(SDL_Joystick *dev, REFGUID rguid, LPDIEFFECTINFOA info);
 HRESULT sdl_input_get_info_W(SDL_Joystick *dev, REFGUID rguid, LPDIEFFECTINFOW info);
 
+#define ITEM_TYPE_BUTTON 1
+#define ITEM_TYPE_AXIS 2
+#define ITEM_TYPE_HAT 3
+
+struct device_state_item {
+    int type;
+    int idx;
+    int val;
+};
+
+typedef BOOL (*enum_device_state_function)(JoystickImpl*, struct device_state_item *, int);
 
 struct SDLDev {
     int id;
@@ -73,6 +84,8 @@ struct SDLDev {
     CHAR *name;
     BOOL is_xbox_gamepad;
 
+    int n_buttons, n_axes, n_hats;
+
     BOOL has_ff, is_joystick;
     int autocenter;
     int gain;
@@ -87,6 +100,8 @@ struct JoystickImpl
     SDL_Joystick *device;
     SDL_Haptic *haptic;
     BOOL ff_paused;
+
+    enum_device_state_function enum_device_state;
 };
 
 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
@@ -190,6 +205,10 @@ static void find_sdldevs(void)
             sdldev.is_xbox_gamepad = is_xinput_device(NULL, sdldev.vendor_id, sdldev.product_id);
         }
 
+        sdldev.n_buttons = SDL_JoystickNumButtons(device);
+        sdldev.n_axes = SDL_JoystickNumAxes(device);
+        sdldev.n_hats = SDL_JoystickNumHats(device);
+
         if (!have_sdldevs)
             new_sdldevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SDLDev));
         else
@@ -300,91 +319,143 @@ static HRESULT sdl_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
   return S_FALSE;
 }
 
+/* straight 1:1 mapping of SDL items and dinput items */
+static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_item *st, int idx)
+{
+    if(idx < This->sdldev->n_buttons)
+    {
+        st->type = ITEM_TYPE_BUTTON;
+        st->idx = idx;
+        st->val = SDL_JoystickGetButton(This->device, idx);
+        return TRUE;
+    }
+
+    idx -= This->sdldev->n_buttons;
+
+    if(idx < This->sdldev->n_axes)
+    {
+        st->type = ITEM_TYPE_AXIS;
+        st->idx = idx;
+        st->val = SDL_JoystickGetAxis(This->device, idx);
+        return TRUE;
+    }
+
+    idx -= This->sdldev->n_axes;
+
+    if(idx < This->sdldev->n_hats)
+    {
+        st->type = ITEM_TYPE_HAT;
+        st->idx = idx;
+        st->val = SDL_JoystickGetHat(This->device, idx);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
 static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
 {
     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
-    int i;
+    int i = 0;
     int inst_id = 0;
     int newVal = 0;
+    struct device_state_item item;
 
     SDL_JoystickUpdate();
 
-    for (i = 0; i < SDL_JoystickNumButtons(This->device); i++)
-    {
-        int val = SDL_JoystickGetButton(This->device, i);
-        int oldVal = This->generic.js.rgbButtons[i];
-        newVal = val ? 0x80 : 0x0;
-        This->generic.js.rgbButtons[i] = newVal;
-        if (oldVal != newVal)
-        {
-            TRACE("Button: %i val %d oldVal %d newVal %d\n",  i, val, oldVal, newVal);
-            inst_id = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
-            queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
-        }
-    }
-    for (i = 0; i < SDL_JoystickNumAxes(This->device); i++)
+    while(This->enum_device_state(This, &item, i++))
     {
-        int oldVal;
-        newVal = SDL_JoystickGetAxis(This->device, i);
-        newVal = joystick_map_axis(&This->generic.props[i], newVal);
-        switch (i)
+        switch(item.type){
+        case ITEM_TYPE_BUTTON:
         {
-            case 0: oldVal = This->generic.js.lX;
-                    This->generic.js.lX  = newVal; break;
-            case 1: oldVal = This->generic.js.lY;
-                    This->generic.js.lY  = newVal; break;
-            case 2: oldVal = This->generic.js.lZ;
-                    This->generic.js.lZ  = newVal; break;
-            case 3: oldVal = This->generic.js.lRx;
-                    This->generic.js.lRx = newVal; break;
-            case 4: oldVal = This->generic.js.lRy;
-                    This->generic.js.lRy = newVal; break;
-            case 5: oldVal = This->generic.js.lRz;
-                    This->generic.js.lRz = newVal; break;
-            case 6: oldVal = This->generic.js.rglSlider[0];
-                    This->generic.js.rglSlider[0] = newVal; break;
-            case 7: oldVal = This->generic.js.rglSlider[1];
-                    This->generic.js.rglSlider[1] = newVal; break;
+            int val = item.val;
+            int oldVal = This->generic.js.rgbButtons[item.idx];
+            newVal = val ? 0x80 : 0x0;
+            This->generic.js.rgbButtons[item.idx] = newVal;
+            if (oldVal != newVal)
+            {
+                TRACE("Button: %i val %d oldVal %d newVal %d\n",  item.idx, val, oldVal, newVal);
+                inst_id = DIDFT_MAKEINSTANCE(item.idx) | DIDFT_PSHBUTTON;
+                queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
+            }
+            break;
         }
-        if (oldVal != newVal)
+
+        case ITEM_TYPE_AXIS:
         {
-            TRACE("Axis: %i oldVal %d newVal %d\n",  i, oldVal, newVal);
-            inst_id = DIDFT_MAKEINSTANCE(i) | DIDFT_ABSAXIS;
-            queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
+            int oldVal;
+            newVal = item.val;
+            newVal = joystick_map_axis(&This->generic.props[item.idx], newVal);
+            switch (item.idx)
+            {
+                case 0: oldVal = This->generic.js.lX;
+                        This->generic.js.lX  = newVal; break;
+                case 1: oldVal = This->generic.js.lY;
+                        This->generic.js.lY  = newVal; break;
+                case 2: oldVal = This->generic.js.lZ;
+                        This->generic.js.lZ  = newVal; break;
+                case 3: oldVal = This->generic.js.lRx;
+                        This->generic.js.lRx = newVal; break;
+                case 4: oldVal = This->generic.js.lRy;
+                        This->generic.js.lRy = newVal; break;
+                case 5: oldVal = This->generic.js.lRz;
+                        This->generic.js.lRz = newVal; break;
+                case 6: oldVal = This->generic.js.rglSlider[0];
+                        This->generic.js.rglSlider[0] = newVal; break;
+                case 7: oldVal = This->generic.js.rglSlider[1];
+                        This->generic.js.rglSlider[1] = newVal; break;
+            }
+            if (oldVal != newVal)
+            {
+                TRACE("Axis: %i oldVal %d newVal %d\n",  item.idx, oldVal, newVal);
+                inst_id = DIDFT_MAKEINSTANCE(item.idx) | DIDFT_ABSAXIS;
+                queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
+            }
+            break;
         }
-    }
-    for (i = 0; i < SDL_JoystickNumHats(This->device); i++)
-    {
-        int oldVal = This->generic.js.rgdwPOV[i];
-        newVal = SDL_JoystickGetHat(This->device, i);
-        switch (newVal)
+
+        case ITEM_TYPE_HAT:
         {
-            case SDL_HAT_CENTERED: newVal = -1; break;
-            case SDL_HAT_UP: newVal = 0; break;
-            case SDL_HAT_RIGHTUP:newVal = 4500; break;
-            case SDL_HAT_RIGHT: newVal = 9000; break;
-            case SDL_HAT_RIGHTDOWN: newVal = 13500; break;
-            case SDL_HAT_DOWN: newVal = 18000; break;
-            case SDL_HAT_LEFTDOWN: newVal = 22500; break;
-            case SDL_HAT_LEFT: newVal = 27000; break;
-            case SDL_HAT_LEFTUP: newVal = 31500; break;
+            int oldVal = This->generic.js.rgdwPOV[item.idx];
+            newVal = item.val;
+            switch (newVal)
+            {
+                case SDL_HAT_CENTERED: newVal = -1; break;
+                case SDL_HAT_UP: newVal = 0; break;
+                case SDL_HAT_RIGHTUP:newVal = 4500; break;
+                case SDL_HAT_RIGHT: newVal = 9000; break;
+                case SDL_HAT_RIGHTDOWN: newVal = 13500; break;
+                case SDL_HAT_DOWN: newVal = 18000; break;
+                case SDL_HAT_LEFTDOWN: newVal = 22500; break;
+                case SDL_HAT_LEFT: newVal = 27000; break;
+                case SDL_HAT_LEFTUP: newVal = 31500; break;
+            }
+            if (oldVal != newVal)
+            {
+                TRACE("Hat : %i oldVal %d newVal %d\n",  item.idx, oldVal, newVal);
+                This->generic.js.rgdwPOV[item.idx] = newVal;
+                inst_id = DIDFT_MAKEINSTANCE(item.idx) | DIDFT_POV;
+                queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
+            }
+            break;
         }
-        if (oldVal != newVal)
-        {
-            TRACE("Hat : %i oldVal %d newVal %d\n",  i, oldVal, newVal);
-            This->generic.js.rgdwPOV[i] = newVal;
-            inst_id = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
-            queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
         }
     }
 }
 
+static enum_device_state_function select_enum_function(struct SDLDev *sdldev)
+{
+    TRACE("for %04x/%04x, using no maps\n", sdldev->vendor_id, sdldev->product_id);
+    return enum_device_state_standard;
+}
+
 static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsigned short index)
 {
     JoystickImpl* newDevice;
     LPDIDATAFORMAT df = NULL;
     DIDEVICEINSTANCEW ddi;
-    int i,idx = 0;
+    int i,idx = 0, axis_count = 0, button_count = 0, hat_count = 0;
+    struct device_state_item item;
 
     newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl));
     if (!newDevice) return NULL;
@@ -394,6 +465,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     newDevice->generic.guidProduct = DInput_PIDVID_Product_GUID;
     newDevice->generic.guidProduct.Data1 = MAKELONG(sdldevs[index].vendor_id, sdldevs[index].product_id);
     newDevice->generic.joy_polldev = poll_sdl_device_state;
+    newDevice->enum_device_state = select_enum_function(&sdldevs[index]);
 
     newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
     newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
@@ -413,9 +485,22 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     newDevice->device = SDL_JoystickOpen(newDevice->sdldev->id);
     newDevice->haptic = SDL_HapticOpenFromJoystick(newDevice->device);
 
-    /* Count number of available axes - supported Axis & POVs */
-    newDevice->generic.devcaps.dwAxes = SDL_JoystickNumAxes(newDevice->device);
+    i = 0;
+    while(newDevice->enum_device_state(newDevice, &item, i++)){
+        switch(item.type){
+            case ITEM_TYPE_BUTTON:
+                ++button_count;
+                break;
+            case ITEM_TYPE_AXIS:
+                ++axis_count;
+                break;
+            case ITEM_TYPE_HAT:
+                ++hat_count;
+                break;
+        }
+    }
 
+    newDevice->generic.devcaps.dwAxes = axis_count;
     if (newDevice->generic.devcaps.dwAxes > 8 )
     {
         WARN("Can't support %d axis. Clamping down to 8\n", newDevice->generic.devcaps.dwAxes);
@@ -432,14 +517,14 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
         newDevice->generic.props[i].lSaturation = 0;
     }
 
-    newDevice->generic.devcaps.dwPOVs = SDL_JoystickNumHats(newDevice->device);
+    newDevice->generic.devcaps.dwPOVs = hat_count;
     if (newDevice->generic.devcaps.dwPOVs > 4)
     {
         WARN("Can't support %d POV. Clamping down to 4\n", newDevice->generic.devcaps.dwPOVs);
         newDevice->generic.devcaps.dwPOVs = 4;
     }
 
-    newDevice->generic.devcaps.dwButtons = SDL_JoystickNumButtons(newDevice->device);
+    newDevice->generic.devcaps.dwButtons = button_count;
     if (newDevice->generic.devcaps.dwButtons > 128)
     {
         WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
From 51993d095e8e564c77d33a9d80d0777a6d6819ee Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 5 Aug 2019 11:38:25 -0500
Subject: [PATCH] dinput: Add mappings for dualshock 4 controllers

---
 dlls/dinput/joystick_sdl.c | 219 +++++++++++++++++++++++++++++++++++++
 1 file changed, 219 insertions(+)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 4233b25dccf..820f95d7a0c 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -319,6 +319,208 @@ static HRESULT sdl_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
   return S_FALSE;
 }
 
+static int buttons_to_sdl_hat(int u, int r, int d, int l)
+{
+    if(u == d)
+    {
+        if(l == r)
+            return SDL_HAT_CENTERED;
+        if(l)
+            return SDL_HAT_LEFT;
+        return SDL_HAT_RIGHT;
+    }
+    if(u)
+    {
+        if(l == r)
+            return SDL_HAT_UP;
+        if(l)
+            return SDL_HAT_LEFTUP;
+        return SDL_HAT_RIGHTUP;
+    }
+    if(l == r)
+        return SDL_HAT_DOWN;
+    if(l)
+        return SDL_HAT_LEFTDOWN;
+    return SDL_HAT_RIGHTDOWN;
+}
+
+/* playstation controllers */
+#define VID_SONY 0x054c
+#define PID_SONY_DUALSHOCK_4 0x05c4
+#define PID_SONY_DUALSHOCK_4_2 0x09cc
+#define PID_SONY_DUALSHOCK_4_DONGLE 0x0ba0
+
+static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_state_item *st, int idx)
+{
+    static const int button_map_ds4_16button[] = {
+        /* [linux button] -> windows button */
+
+        /* [0] -> */ 1, /* cross */
+        /* [1] -> */ 2, /* circle */
+        /* [2] -> */ 0, /* square */
+        /* [3] -> */ 3, /* triangle */
+
+        /* [4] -> */ 8, /* share */
+        /* [5] -> */ 12, /* guide */
+        /* [6] -> */ 9, /* options */
+
+        /* [7] -> */ 10, /* L3 */
+        /* [8] -> */ 11, /* R3 */
+
+        /* [9] -> */ 4, /* L1 */
+        /* [10] -> */ 5, /* R1 */
+    };
+
+    static const int axis_map_ds4_16button[] = {
+        /* [linux axis] -> windows axis */
+
+        /* [0] -> */ 0, /* left horiz */
+        /* [1] -> */ 1, /* left vert */
+        /* [2] -> */ 2, /* right horiz */
+        /* [3] -> */ 5, /* right vert */
+        /* [4] -> */ 3, /* L2 */
+        /* [5] -> */ 4, /* R2 */
+    };
+
+    static const int DNP_TOUCHPAD_BUTTON = 13;
+    static const int SDL_TOUCHPAD_BUTTON = 15;
+
+    static const int DNP_L2_BUTTON = 6;
+    static const int SDL_L2_AXIS = 4;
+
+    static const int DNP_R2_BUTTON = 7;
+    static const int SDL_R2_AXIS = 5;
+
+    static const int SDL_DPAD_UP_BUTTON = 11;
+    static const int SDL_DPAD_DOWN_BUTTON = 12;
+    static const int SDL_DPAD_LEFT_BUTTON = 13;
+    static const int SDL_DPAD_RIGHT_BUTTON = 14;
+
+    if(idx < 11)
+    {
+        /* first 11 buttons */
+        st->type = ITEM_TYPE_BUTTON;
+        st->idx = button_map_ds4_16button[idx];
+        st->val = SDL_JoystickGetButton(This->device, idx);
+        return TRUE;
+    }
+
+    if(idx < 17)
+    {
+        /* six axes */
+        idx -= 11;
+        st->type = ITEM_TYPE_AXIS;
+        st->idx = axis_map_ds4_16button[idx];
+        st->val = SDL_JoystickGetAxis(This->device, idx);
+        return TRUE;
+    }
+
+    switch(idx)
+    {
+    case 17:
+        /* touchpad button */
+        st->type = ITEM_TYPE_BUTTON;
+        st->idx = DNP_TOUCHPAD_BUTTON;
+        st->val = SDL_JoystickGetButton(This->device, SDL_TOUCHPAD_BUTTON);
+        return TRUE;
+
+    case 18:
+        /* L2 button */
+        st->type = ITEM_TYPE_BUTTON;
+        st->idx = DNP_L2_BUTTON;
+        /* turn button on at about 1/8 of the trigger travel */
+        st->val = SDL_JoystickGetAxis(This->device, SDL_L2_AXIS) > 3 * SDL_JOYSTICK_AXIS_MIN / 4;
+        return TRUE;
+
+    case 19:
+        /* R2 button */
+        st->type = ITEM_TYPE_BUTTON;
+        st->idx = DNP_R2_BUTTON;
+        /* turn button on at about 1/8 of the trigger travel */
+        st->val = SDL_JoystickGetAxis(This->device, SDL_R2_AXIS) > 3 * SDL_JOYSTICK_AXIS_MIN / 4;
+        return TRUE;
+
+    case 20:
+        /* dpad buttons --> hatswitch */
+        st->type = ITEM_TYPE_HAT;
+        st->idx = 0;
+        st->val = buttons_to_sdl_hat(
+                SDL_JoystickGetButton(This->device, SDL_DPAD_UP_BUTTON),
+                SDL_JoystickGetButton(This->device, SDL_DPAD_RIGHT_BUTTON),
+                SDL_JoystickGetButton(This->device, SDL_DPAD_DOWN_BUTTON),
+                SDL_JoystickGetButton(This->device, SDL_DPAD_LEFT_BUTTON));
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static BOOL enum_device_state_ds4_13button(JoystickImpl *This, struct device_state_item *st, int idx)
+{
+    static const int button_map_ds4_13button[] = {
+        /* [linux button] -> windows button */
+
+        /* [0] -> */ 1, /* cross */
+        /* [1] -> */ 2, /* circle */
+        /* [2] -> */ 3, /* triangle */
+        /* [3] -> */ 0, /* square */
+
+        /* [4] -> */ 4, /* L1 */
+        /* [5] -> */ 5, /* R1 */
+        /* [6] -> */ 6, /* L2 */
+        /* [7] -> */ 7, /* R2 */
+        /* [8] -> */ 8, /* share */
+        /* [9] -> */ 9, /* options */
+
+        /* [10] -> */ 12, /* guide */
+        /* [11] -> */ 10, /* L3 */
+        /* [12] -> */ 11, /* R3 */
+
+        /* ps4 controller through linux event API does not support touchpad button */
+    };
+
+    static const int axis_map_ds4_13button[] = {
+        /* [linux axis] -> windows axis */
+
+        /* [0] -> */ 0, /* left horiz */
+        /* [1] -> */ 1, /* left vert */
+        /* [2] -> */ 3, /* L2 */
+        /* [3] -> */ 2, /* right horiz */
+        /* [4] -> */ 5, /* right vert */
+        /* [5] -> */ 4, /* R2 */
+    };
+
+    if(idx < This->sdldev->n_buttons)
+    {
+        st->type = ITEM_TYPE_BUTTON;
+        st->idx = button_map_ds4_13button[idx];
+        st->val = SDL_JoystickGetButton(This->device, idx);
+        return TRUE;
+    }
+
+    idx -= This->sdldev->n_buttons;
+
+    if(idx < This->sdldev->n_axes)
+    {
+        st->type = ITEM_TYPE_AXIS;
+        st->idx = axis_map_ds4_13button[idx];
+        st->val = SDL_JoystickGetAxis(This->device, idx);
+        return TRUE;
+    }
+
+    idx -= This->sdldev->n_axes;
+
+    if(idx < This->sdldev->n_hats)
+    {
+        st->type = ITEM_TYPE_HAT;
+        st->idx = idx;
+        st->val = SDL_JoystickGetHat(This->device, idx);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
 /* straight 1:1 mapping of SDL items and dinput items */
 static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_item *st, int idx)
 {
@@ -445,6 +647,23 @@ static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
 
 static enum_device_state_function select_enum_function(struct SDLDev *sdldev)
 {
+    switch(sdldev->vendor_id){
+    case VID_SONY:
+        switch(sdldev->product_id){
+        case PID_SONY_DUALSHOCK_4:
+        case PID_SONY_DUALSHOCK_4_2:
+        case PID_SONY_DUALSHOCK_4_DONGLE:
+            TRACE("for %04x/%04x, polling ds4 controller\n", sdldev->vendor_id, sdldev->product_id);
+            if(sdldev->n_buttons >= 16)
+                return enum_device_state_ds4_16button;
+
+            TRACE("SDL only reports %u buttons for this PS4 controller. Please upgrade SDL to > 2.0.10 and/or give your user hidraw access.\n",
+                    sdldev->n_buttons);
+            return enum_device_state_ds4_13button;
+        }
+        break;
+    }
+
     TRACE("for %04x/%04x, using no maps\n", sdldev->vendor_id, sdldev->product_id);
     return enum_device_state_standard;
 }
From 13a93703ca2a57325845bc4678b8e9db325f8987 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Wed, 21 Aug 2019 10:28:44 -0500
Subject: [PATCH] dinput: Implement SDL GetDeviceInfoW on top of GetDeviceInfoA

---
 dlls/dinput/joystick_sdl.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 820f95d7a0c..a309c6b617f 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -225,7 +225,7 @@ static void find_sdldevs(void)
     }
 }
 
-static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
+static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
 {
     DWORD dwSize = lpddi->dwSize;
 
@@ -252,31 +252,31 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
             lpddi->wUsage = 0x05; /* Game Pad */
     }
 
-    MultiByteToWideChar(CP_ACP, 0, sdldevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
-    MultiByteToWideChar(CP_ACP, 0, sdldevs[id].name, -1, lpddi->tszProductName, MAX_PATH);
+    strcpy(lpddi->tszInstanceName, sdldevs[id].name);
+    strcpy(lpddi->tszProductName,  sdldevs[id].name);
 }
 
-static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
+static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
 {
-    DIDEVICEINSTANCEW lpddiW;
+    DIDEVICEINSTANCEA lpddiA;
     DWORD dwSize = lpddi->dwSize;
 
-    lpddiW.dwSize = sizeof(lpddiW);
-    fill_joystick_dideviceinstanceW(&lpddiW, version, id);
+    lpddiA.dwSize = sizeof(lpddiA);
+    fill_joystick_dideviceinstanceA(&lpddiA, version, id);
 
     TRACE("%d %p\n", dwSize, lpddi);
     memset(lpddi, 0, dwSize);
 
-    /* Convert W->A */
+    /* Convert A->W */
     lpddi->dwSize = dwSize;
-    lpddi->guidInstance = lpddiW.guidInstance;
-    lpddi->guidProduct = lpddiW.guidProduct;
-    lpddi->dwDevType = lpddiW.dwDevType;
-    strcpy(lpddi->tszInstanceName, sdldevs[id].name);
-    strcpy(lpddi->tszProductName,  sdldevs[id].name);
-    lpddi->guidFFDriver = lpddiW.guidFFDriver;
-    lpddi->wUsagePage = lpddiW.wUsagePage;
-    lpddi->wUsage = lpddiW.wUsage;
+    lpddi->guidInstance = lpddiA.guidInstance;
+    lpddi->guidProduct = lpddiA.guidProduct;
+    lpddi->dwDevType = lpddiA.dwDevType;
+    MultiByteToWideChar(CP_ACP, 0, lpddiA.tszInstanceName, -1, lpddi->tszInstanceName, MAX_PATH);
+    MultiByteToWideChar(CP_ACP, 0, lpddiA.tszProductName, -1, lpddi->tszProductName, MAX_PATH);
+    lpddi->guidFFDriver = lpddiA.guidFFDriver;
+    lpddi->wUsagePage = lpddiA.wUsagePage;
+    lpddi->wUsage = lpddiA.wUsage;
 }
 
 static HRESULT sdl_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
From 7f936bd5b7d7dfbd779371d034acb7775bdeb0ab Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Wed, 21 Aug 2019 10:25:12 -0500
Subject: [PATCH] dinput: Add device info overrides for dualshock 4

---
 dlls/dinput/joystick_sdl.c | 60 +++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 8 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index a309c6b617f..53bc50d1639 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -56,6 +56,11 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 
+#define VID_SONY 0x054c
+#define PID_SONY_DUALSHOCK_4 0x05c4
+#define PID_SONY_DUALSHOCK_4_2 0x09cc
+#define PID_SONY_DUALSHOCK_4_DONGLE 0x0ba0
+
 typedef struct JoystickImpl JoystickImpl;
 static const IDirectInputDevice8AVtbl JoystickAvt;
 static const IDirectInputDevice8WVtbl JoystickWvt;
@@ -225,9 +230,30 @@ static void find_sdldevs(void)
     }
 }
 
+static struct device_info_override {
+    WORD vid;
+    WORD pid;
+    const char *instance_name;
+    const char *product_name;
+    DWORD dev_type;
+    DWORD dev_type8;
+} device_info_overrides[] = {
+    { VID_SONY, PID_SONY_DUALSHOCK_4, "Wireless Controller", "Wireless Controller",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_1STPERSON | (DI8DEVTYPE1STPERSON_SIXDOF << 8) },
+
+    { VID_SONY, PID_SONY_DUALSHOCK_4_2, "Wireless Controller", "Wireless Controller",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_1STPERSON | (DI8DEVTYPE1STPERSON_SIXDOF << 8) },
+
+    { VID_SONY, PID_SONY_DUALSHOCK_4_DONGLE, "Wireless Controller", "Wireless Controller",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_1STPERSON | (DI8DEVTYPE1STPERSON_SIXDOF << 8) },
+};
+
 static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
 {
-    DWORD dwSize = lpddi->dwSize;
+    DWORD dwSize = lpddi->dwSize, i;
 
     TRACE("%d %p\n", dwSize, lpddi);
     memset(lpddi, 0, dwSize);
@@ -252,8 +278,31 @@ static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD ver
             lpddi->wUsage = 0x05; /* Game Pad */
     }
 
-    strcpy(lpddi->tszInstanceName, sdldevs[id].name);
-    strcpy(lpddi->tszProductName,  sdldevs[id].name);
+    for(i = 0; i < ARRAY_SIZE(device_info_overrides); ++i)
+    {
+        const struct device_info_override *override = &device_info_overrides[i];
+        if(sdldevs[id].vendor_id == override->vid &&
+                sdldevs[id].product_id == override->pid)
+        {
+            TRACE("found devinfo override for %04hx/%04hx\n",
+                    override->vid, override->pid);
+            if(version >= 0x800)
+                lpddi->dwDevType = override->dev_type8;
+            else
+                lpddi->dwDevType = override->dev_type;
+
+            strcpy(lpddi->tszInstanceName, override->instance_name);
+            strcpy(lpddi->tszProductName,  override->product_name);
+
+            break;
+        }
+    }
+
+    if(i >= ARRAY_SIZE(device_info_overrides))
+    {
+        strcpy(lpddi->tszInstanceName, sdldevs[id].name);
+        strcpy(lpddi->tszProductName,  sdldevs[id].name);
+    }
 }
 
 static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
@@ -345,11 +394,6 @@ static int buttons_to_sdl_hat(int u, int r, int d, int l)
 }
 
 /* playstation controllers */
-#define VID_SONY 0x054c
-#define PID_SONY_DUALSHOCK_4 0x05c4
-#define PID_SONY_DUALSHOCK_4_2 0x09cc
-#define PID_SONY_DUALSHOCK_4_DONGLE 0x0ba0
-
 static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_state_item *st, int idx)
 {
     static const int button_map_ds4_16button[] = {

From 8be1ea8cd6f343dc566c131f5a08792f57125ccb Mon Sep 17 00:00:00 2001
From: Alexey Prokhin <alexey@prokhin.ru>
Date: Tue, 27 Aug 2019 20:42:07 +0300
Subject: [PATCH] dinput: Remove is_xbox_gamepad flag from SDL joysticks

---
 dlls/dinput/joystick_sdl.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 53bc50d1639..228447085d1 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -87,7 +87,6 @@ struct SDLDev {
     WORD vendor_id;
     WORD product_id;
     CHAR *name;
-    BOOL is_xbox_gamepad;
 
     int n_buttons, n_axes, n_hats;
 
@@ -206,8 +205,6 @@ static void find_sdldevs(void)
                 type == SDL_JOYSTICK_TYPE_WHEEL ||
                 type == SDL_JOYSTICK_TYPE_FLIGHT_STICK ||
                 type == SDL_JOYSTICK_TYPE_THROTTLE;
-
-            sdldev.is_xbox_gamepad = is_xinput_device(NULL, sdldev.vendor_id, sdldev.product_id);
         }
 
         sdldev.n_buttons = SDL_JoystickNumButtons(device);
@@ -1011,14 +1008,16 @@ static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF
             static const WCHAR miW[] = {'m','i',0};
             static const WCHAR igW[] = {'i','g',0};
 
+            BOOL is_gamepad;
             LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
 
             if (!This->sdldev->product_id || !This->sdldev->vendor_id)
                 return DIERR_UNSUPPORTED;
 
+            is_gamepad = is_xinput_device(&This->generic.devcaps, This->sdldev->vendor_id, This->sdldev->product_id);
             pd->guidClass = GUID_DEVCLASS_HIDCLASS;
             sprintfW(pd->wszPath, formatW, This->sdldev->vendor_id, This->sdldev->product_id,
-                     This->sdldev->is_xbox_gamepad ? igW : miW, This->sdldev->id);
+                     is_gamepad ? igW : miW, This->sdldev->id);
 
             TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
             break;
From c3ea2e128b2933116aa61ba8b8df99dcf84179a7 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Fri, 30 Aug 2019 09:20:46 -0500
Subject: [PATCH] dinput: Don't enumerate joysticks that are blacklisted

SDL only respects these variables for game controllers. All joysticks
are allowed through. But we don't want to present these at all, so we'll
check the variables manually.
---
 dlls/dinput/joystick_sdl.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 228447085d1..4bb218a7d59 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -135,6 +135,35 @@ static const GUID DInput_PIDVID_Product_GUID = { /* PIDVID-0000-0000-0000-504944
 static int have_sdldevs = -1;
 static struct SDLDev *sdldevs = NULL;
 
+/* logic from SDL2's SDL_ShouldIgnoreGameController */
+static BOOL is_in_sdl_blacklist(DWORD vid, DWORD pid)
+{
+    char needle[16];
+    const char *blacklist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES");
+    const char *whitelist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT");
+    const char *allow_virtual = getenv("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD");
+
+    if (!blacklist && !whitelist)
+        return FALSE;
+
+    if (allow_virtual && *allow_virtual != '0')
+    {
+        if(vid == 0x28DE && pid == 0x11FF)
+            return FALSE;
+    }
+
+    if (whitelist)
+    {
+        sprintf(needle, "0x%04x/0x%04x", vid, pid);
+
+        return strcasestr(whitelist, needle) == NULL;
+    }
+
+    sprintf(needle, "0x%04x/0x%04x", vid, pid);
+
+    return strcasestr(blacklist, needle) != NULL;
+}
+
 static void find_sdldevs(void)
 {
     int i;
@@ -175,6 +204,7 @@ static void find_sdldevs(void)
 
         if (device_disabled_registry(sdldev.name)) {
             SDL_JoystickClose(device);
+            HeapFree(GetProcessHeap(), 0, sdldev.name);
             continue;
         }
 
@@ -199,6 +229,13 @@ static void find_sdldevs(void)
             sdldev.product_id = SDL_JoystickInstanceID(device) + 1;
         }
 
+        if(is_in_sdl_blacklist(sdldev.vendor_id, sdldev.product_id))
+        {
+            TRACE("joystick %04x/%04x is in SDL blacklist, ignoring\n", sdldev.vendor_id, sdldev.product_id);
+            SDL_JoystickClose(device);
+            continue;
+        }
+
         {
             SDL_JoystickType type = SDL_JoystickGetType(device);
             sdldev.is_joystick =
From 127feb7008e91fa3a622667bd03081d423306606 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 29 Aug 2019 15:04:36 -0500
Subject: [PATCH] winebus.sys: Ignore blacklisted SDL joysticks, too

SDL only respects these variables for game controllers. All joysticks
are allowed through. But we don't want to present these at all, so we'll
check the variables manually.
---
 dlls/winebus.sys/bus_sdl.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index 7423aa57fe4..5f0f5116e9e 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -991,6 +991,35 @@ static void try_remove_device(SDL_JoystickID id)
         pSDL_HapticClose(sdl_haptic);
 }
 
+/* logic from SDL2's SDL_ShouldIgnoreGameController */
+static BOOL is_in_sdl_blacklist(DWORD vid, DWORD pid)
+{
+    char needle[16];
+    const char *blacklist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES");
+    const char *whitelist = getenv("SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT");
+    const char *allow_virtual = getenv("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD");
+
+    if (!blacklist && !whitelist)
+        return FALSE;
+
+    if (allow_virtual && *allow_virtual != '0')
+    {
+        if(vid == 0x28DE && pid == 0x11FF)
+            return FALSE;
+    }
+
+    if (whitelist)
+    {
+        sprintf(needle, "0x%04x/0x%04x", vid, pid);
+
+        return strcasestr(whitelist, needle) == NULL;
+    }
+
+    sprintf(needle, "0x%04x/0x%04x", vid, pid);
+
+    return strcasestr(blacklist, needle) != NULL;
+}
+
 static void try_add_device(unsigned int index, BOOL xinput_hack)
 {
     DWORD vid = 0, pid = 0, version = 0;
@@ -1030,6 +1059,12 @@ static void try_add_device(unsigned int index, BOOL xinput_hack)
         return;
     }
 
+    if(is_in_sdl_blacklist(vid, pid))
+    {
+        TRACE("device %04x/%04x is in blacklist, ignoring\n", vid, pid);
+        return;
+    }
+
     if (map_controllers && pSDL_IsGameController(index))
         controller = pSDL_GameControllerOpen(index);
 
From 55e97a0d5d40149cf071e9db1e2da307448b222e Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Fri, 30 Aug 2019 10:20:16 -0500
Subject: [PATCH] winebus.sys: Override Steam virtual controller vid/pid with
 Xbox

Matches Windows Steam client behavior.
---
 dlls/winebus.sys/bus_sdl.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c
index 5f0f5116e9e..c16ed5658da 100644
--- a/dlls/winebus.sys/bus_sdl.c
+++ b/dlls/winebus.sys/bus_sdl.c
@@ -1091,6 +1091,13 @@ static void try_add_device(unsigned int index, BOOL xinput_hack)
         TRACE("Found sdl game controller 0x%x (vid %04x, pid %04x, version %u, serial %s, xinput_hack: %u)\n",
               id, vid, pid, version, debugstr_w(serial), xinput_hack);
         is_xbox_gamepad = TRUE;
+
+        if(vid == 0x28DE && pid == 0x11FF)
+        {
+            TRACE("Steam virtual controller, pretending it's an Xbox 360 controller\n");
+            vid = 0x045e;
+            pid = 0x028e;
+        }
     }
     else
     {
From df2ab81621f9ab05cd3763c29efe8bef49f7d6d8 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Fri, 30 Aug 2019 10:58:16 -0500
Subject: [PATCH] dinput: Override Steam virtual controller name

---
 dlls/dinput/joystick_sdl.c | 53 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 52 insertions(+), 1 deletion(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 4bb218a7d59..97602160ff5 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -61,6 +61,19 @@ WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 #define PID_SONY_DUALSHOCK_4_2 0x09cc
 #define PID_SONY_DUALSHOCK_4_DONGLE 0x0ba0
 
+#define VID_VALVE 0x28de
+#define PID_VALVE_VIRTUAL_CONTROLLER 0x11ff
+
+#define VID_MICROSOFT 0x045e
+#define PID_MICROSOFT_XBOX_360 0x028e
+#define PID_MICROSOFT_XBOX_360_WIRELESS 0x028f
+#define PID_MICROSOFT_XBOX_360_ADAPTER  0x0719
+#define PID_MICROSOFT_XBOX_ONE 0x02d1
+#define PID_MICROSOFT_XBOX_ONE_CF 0x02dd
+#define PID_MICROSOFT_XBOX_ONE_ELITE 0x02e3
+#define PID_MICROSOFT_XBOX_ONE_S 0x02ea
+#define PID_MICROSOFT_XBOX_ONE_S_2 0x02fd
+
 typedef struct JoystickImpl JoystickImpl;
 static const IDirectInputDevice8AVtbl JoystickAvt;
 static const IDirectInputDevice8WVtbl JoystickWvt;
@@ -148,7 +161,7 @@ static BOOL is_in_sdl_blacklist(DWORD vid, DWORD pid)
 
     if (allow_virtual && *allow_virtual != '0')
     {
-        if(vid == 0x28DE && pid == 0x11FF)
+        if(vid == VID_VALVE && pid == PID_VALVE_VIRTUAL_CONTROLLER)
             return FALSE;
     }
 
@@ -236,6 +249,12 @@ static void find_sdldevs(void)
             continue;
         }
 
+        if(sdldev.vendor_id == VID_VALVE && sdldev.product_id == PID_VALVE_VIRTUAL_CONTROLLER)
+        {
+            sdldev.vendor_id = VID_MICROSOFT;
+            sdldev.product_id = PID_MICROSOFT_XBOX_360;
+        }
+
         {
             SDL_JoystickType type = SDL_JoystickGetType(device);
             sdldev.is_joystick =
@@ -283,6 +302,38 @@ static struct device_info_override {
     { VID_SONY, PID_SONY_DUALSHOCK_4_DONGLE, "Wireless Controller", "Wireless Controller",
         DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
         DIDEVTYPE_HID | DI8DEVTYPE_1STPERSON | (DI8DEVTYPE1STPERSON_SIXDOF << 8) },
+
+    { VID_MICROSOFT, PID_MICROSOFT_XBOX_360, "Controller (XBOX 360 For Windows)", "Controller (XBOX 360 For Windows)",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8) },
+
+    { VID_MICROSOFT, PID_MICROSOFT_XBOX_360_WIRELESS, "Controller (XBOX 360 For Windows)", "Controller (XBOX 360 For Windows)",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8) },
+
+    { VID_MICROSOFT, PID_MICROSOFT_XBOX_360_ADAPTER, "Controller (XBOX 360 For Windows)", "Controller (XBOX 360 For Windows)",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8) },
+
+    { VID_MICROSOFT, PID_MICROSOFT_XBOX_ONE, "Controller (XBOX One For Windows)", "Controller (XBOX One For Windows)",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8) },
+
+    { VID_MICROSOFT, PID_MICROSOFT_XBOX_ONE_CF, "Controller (XBOX One For Windows)", "Controller (XBOX One For Windows)",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8) },
+
+    { VID_MICROSOFT, PID_MICROSOFT_XBOX_ONE_ELITE, "Controller (XBOX One For Windows)", "Controller (XBOX One For Windows)",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8) },
+
+    { VID_MICROSOFT, PID_MICROSOFT_XBOX_ONE_S, "Controller (XBOX One For Windows)", "Controller (XBOX One For Windows)",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8) },
+
+    { VID_MICROSOFT, PID_MICROSOFT_XBOX_ONE_S_2, "Controller (XBOX One For Windows)", "Controller (XBOX One For Windows)",
+        DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8),
+        DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8) },
 };
 
 static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
From 90411d5e728ad5d92925d3ca76593e48fba6b5ff Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 29 Aug 2019 11:20:23 -0500
Subject: [PATCH] winebus: Extract bluetooth info from uevent

udev doesn't report this info.
---
 dlls/winebus.sys/bus_udev.c | 167 ++++++++++++++++++++++--------------
 1 file changed, 101 insertions(+), 66 deletions(-)

diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index 5439513b048..c5aab2a8ad6 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -114,6 +114,8 @@ struct platform_private
     int control_pipe[2];
 
     struct vidpid vidpid;
+
+    DWORD bus_type;
 };
 
 static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *device)
@@ -656,6 +658,68 @@ static WCHAR *get_sysattr_string(struct udev_device *dev, const char *sysattr)
     return strdupAtoW(attr);
 }
 
+static void parse_uevent_info(const char *uevent, DWORD *bus_type, DWORD *vendor_id,
+                             DWORD *product_id, WORD *input, WCHAR **serial_number, WCHAR **product)
+{
+    char *tmp;
+    char *saveptr = NULL;
+    char *line;
+    char *key;
+    char *value;
+
+    tmp = heap_alloc(strlen(uevent) + 1);
+    strcpy(tmp, uevent);
+    line = strtok_r(tmp, "\n", &saveptr);
+    while (line != NULL)
+    {
+        /* line: "KEY=value" */
+        key = line;
+        value = strchr(line, '=');
+        if (!value)
+        {
+            goto next_line;
+        }
+        *value = '\0';
+        value++;
+
+        if (strcmp(key, "HID_ID") == 0)
+        {
+            /**
+             *        type vendor   product
+             * HID_ID=0003:000005AC:00008242
+             **/
+            sscanf(value, "%x:%x:%x", bus_type, vendor_id, product_id);
+        }
+        else if (strcmp(key, "HID_UNIQ") == 0)
+        {
+            /* The caller has to free the serial number */
+            if (*value)
+            {
+                *serial_number = strdupAtoW(value);
+            }
+        }
+        else if (product && strcmp(key, "HID_NAME") == 0)
+        {
+            /* The caller has to free the product name */
+            if (*value)
+            {
+                *product = strdupAtoW(value);
+            }
+        }
+        else if (strcmp(key, "HID_PHYS") == 0)
+        {
+            const char *input_no = strstr(value, "input");
+            if (input_no)
+                *input = atoi(input_no+5 );
+        }
+
+next_line:
+        line = strtok_r(NULL, "\n", &saveptr);
+    }
+
+    heap_free(tmp);
+}
+
 static int compare_platform_device(DEVICE_OBJECT *device, void *platform_dev)
 {
     struct udev_device *dev1 = impl_from_DEVICE_OBJECT(device)->udev_device;
@@ -697,12 +761,43 @@ static NTSTATUS hidraw_get_reportdescriptor(DEVICE_OBJECT *device, BYTE *buffer,
 
 static NTSTATUS hidraw_get_string(DEVICE_OBJECT *device, DWORD index, WCHAR *buffer, DWORD length)
 {
-    struct udev_device *usbdev;
+    struct udev_device *usbdev, *hiddev;
     struct platform_private *private = impl_from_DEVICE_OBJECT(device);
     WCHAR *str = NULL;
 
+    hiddev = udev_device_get_parent_with_subsystem_devtype(private->udev_device, "hid", NULL);
     usbdev = udev_device_get_parent_with_subsystem_devtype(private->udev_device, "usb", "usb_device");
-    if (usbdev)
+
+    if (private->bus_type == BUS_BLUETOOTH && hiddev)
+    {
+        DWORD bus_type, vid, pid;
+        WORD input;
+        WCHAR *serial = NULL, *product = NULL;
+
+        /* udev doesn't report this info, so we have to extract it from uevent property */
+
+        parse_uevent_info(udev_device_get_sysattr_value(hiddev, "uevent"),
+                &bus_type, &vid, &pid, &input, &serial, &product);
+
+        switch (index)
+        {
+            case HID_STRING_ID_IPRODUCT:
+                str = product;
+                HeapFree(GetProcessHeap(), 0, serial);
+                break;
+            case HID_STRING_ID_IMANUFACTURER:
+                /* TODO */
+                break;
+            case HID_STRING_ID_ISERIALNUMBER:
+                str = serial;
+                HeapFree(GetProcessHeap(), 0, product);
+                break;
+            default:
+                ERR("Unhandled string index %08x\n", index);
+                return STATUS_NOT_IMPLEMENTED;
+        }
+    }
+    else if (usbdev)
     {
         switch (index)
         {
@@ -1069,68 +1164,6 @@ static int check_same_device(DEVICE_OBJECT *device, void* context)
     return !compare_platform_device(device, context);
 }
 
-static int parse_uevent_info(const char *uevent, DWORD *vendor_id,
-                             DWORD *product_id, WORD *input, WCHAR **serial_number)
-{
-    DWORD bus_type;
-    char *tmp;
-    char *saveptr = NULL;
-    char *line;
-    char *key;
-    char *value;
-
-    int found_id = 0;
-    int found_serial = 0;
-
-    tmp = heap_alloc(strlen(uevent) + 1);
-    strcpy(tmp, uevent);
-    line = strtok_r(tmp, "\n", &saveptr);
-    while (line != NULL)
-    {
-        /* line: "KEY=value" */
-        key = line;
-        value = strchr(line, '=');
-        if (!value)
-        {
-            goto next_line;
-        }
-        *value = '\0';
-        value++;
-
-        if (strcmp(key, "HID_ID") == 0)
-        {
-            /**
-             *        type vendor   product
-             * HID_ID=0003:000005AC:00008242
-             **/
-            int ret = sscanf(value, "%x:%x:%x", &bus_type, vendor_id, product_id);
-            if (ret == 3)
-                found_id = 1;
-        }
-        else if (strcmp(key, "HID_UNIQ") == 0)
-        {
-            /* The caller has to free the serial number */
-            if (*value)
-            {
-                *serial_number = strdupAtoW(value);
-                found_serial = 1;
-            }
-        }
-        else if (strcmp(key, "HID_PHYS") == 0)
-        {
-            const char *input_no = strstr(value, "input");
-            if (input_no)
-                *input = atoi(input_no+5 );
-        }
-
-next_line:
-        line = strtok_r(NULL, "\n", &saveptr);
-    }
-
-    heap_free(tmp);
-    return (found_id && found_serial);
-}
-
 static DWORD a_to_bcd(const char *s)
 {
     DWORD r = 0;
@@ -1181,7 +1214,7 @@ static BOOL is_in_sdl_blacklist(DWORD vid, DWORD pid)
 
 static void try_add_device(struct udev_device *dev)
 {
-    DWORD vid = 0, pid = 0, version = 0;
+    DWORD vid = 0, pid = 0, version = 0, bus_type = 0;
     struct udev_device *hiddev = NULL, *walk_device;
     DEVICE_OBJECT *device = NULL;
     const char *subsystem;
@@ -1224,7 +1257,7 @@ static void try_add_device(struct udev_device *dev)
         }
 #endif
         parse_uevent_info(udev_device_get_sysattr_value(hiddev, "uevent"),
-                          &vid, &pid, &input, &serial);
+                          &bus_type, &vid, &pid, &input, &serial, NULL);
         if (serial == NULL)
             serial = strdupAtoW(base_serial);
 
@@ -1254,6 +1287,7 @@ static void try_add_device(struct udev_device *dev)
         vid = device_id.vendor;
         pid = device_id.product;
         version = device_id.version;
+        bus_type = device_id.bustype;
     }
 #else
     else
@@ -1311,6 +1345,7 @@ static void try_add_device(struct udev_device *dev)
         private->device_fd = fd;
         private->vidpid.vid = vid;
         private->vidpid.pid = pid;
+        private->bus_type = bus_type;
 #ifdef HAS_PROPER_INPUT_HEADER
         if (strcmp(subsystem, "input") == 0)
             if (!build_report_descriptor((struct wine_input_private*)private, dev))
From b6f1dec81ef801ac40d36fcbf52477103c12e4e4 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Fri, 30 Aug 2019 12:12:26 -0500
Subject: [PATCH] winebus.sys: Bluetooth doesn't report USB device version

---
 dlls/winebus.sys/bus_udev.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index c5aab2a8ad6..b3889ad15c9 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -1261,15 +1261,18 @@ static void try_add_device(struct udev_device *dev)
         if (serial == NULL)
             serial = strdupAtoW(base_serial);
 
-        walk_device = dev;
-        while (walk_device && !bcdDevice)
+        if(bus_type != BUS_BLUETOOTH)
         {
-            bcdDevice = udev_device_get_sysattr_value(walk_device, "bcdDevice");
-            walk_device = udev_device_get_parent(walk_device);
-        }
-        if (bcdDevice)
-        {
-            version = a_to_bcd(bcdDevice);
+            walk_device = dev;
+            while (walk_device && !bcdDevice)
+            {
+                bcdDevice = udev_device_get_sysattr_value(walk_device, "bcdDevice");
+                walk_device = udev_device_get_parent(walk_device);
+            }
+            if (bcdDevice)
+            {
+                version = a_to_bcd(bcdDevice);
+            }
         }
     }
 #ifdef HAS_PROPER_INPUT_HEADER
From 29e248dd3c18a26c098dcc87796130e98989fa2d Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 3 Sep 2019 10:49:53 -0500
Subject: [PATCH] dinput: Return real rawinput path for dinput device

---
 dlls/dinput/joystick_sdl.c | 72 ++++++++++++++++++++++++++++++++------
 1 file changed, 61 insertions(+), 11 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 97602160ff5..219f7743826 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "wine/port.h"
 
+#define NONAMELESSUNION
 #include <assert.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -1091,23 +1092,72 @@ static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF
 
         case (DWORD_PTR) DIPROP_GUIDANDPATH:
         {
-            static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
-                                            'p','i','d','_','%','0','4','x','&','%','s','_','%','i',0};
-            static const WCHAR miW[] = {'m','i',0};
-            static const WCHAR igW[] = {'i','g',0};
-
-            BOOL is_gamepad;
+            RAWINPUTDEVICELIST *list;
+            RID_DEVICE_INFO info;
+            UINT ndevs, i, ur, size;
             LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
 
+            memset(pd, 0, sizeof(*pd));
+
             if (!This->sdldev->product_id || !This->sdldev->vendor_id)
                 return DIERR_UNSUPPORTED;
 
-            is_gamepad = is_xinput_device(&This->generic.devcaps, This->sdldev->vendor_id, This->sdldev->product_id);
-            pd->guidClass = GUID_DEVCLASS_HIDCLASS;
-            sprintfW(pd->wszPath, formatW, This->sdldev->vendor_id, This->sdldev->product_id,
-                     is_gamepad ? igW : miW, This->sdldev->id);
+            ur = GetRawInputDeviceList(NULL, &ndevs, sizeof(RAWINPUTDEVICELIST));
+            if (ur == (UINT)-1)
+                return DIERR_GENERIC;
+
+            list = HeapAlloc(GetProcessHeap(), 0, ndevs * sizeof(*list));
+            if (!list)
+                return DIERR_OUTOFMEMORY;
+
+            ndevs = GetRawInputDeviceList(list, &ndevs, sizeof(RAWINPUTDEVICELIST));
+            if (ndevs == (UINT)-1)
+            {
+                HeapFree(GetProcessHeap(), 0, list);
+                return DIERR_GENERIC;
+            }
+
+            for (i = 0; i < ndevs; ++i)
+            {
+                if (list[i].dwType != RIM_TYPEHID)
+                    continue;
+
+                memset(&info, 0, sizeof(info));
+                size = info.cbSize = sizeof(info);
+
+                ur = GetRawInputDeviceInfoW(list[i].hDevice, RIDI_DEVICEINFO, &info, &size);
+                TRACE("got hid: %04x/%04x\n", info.u.hid.dwVendorId,
+                        info.u.hid.dwProductId);
+                if (ur == (UINT)-1 ||
+                        (info.u.hid.dwVendorId != This->sdldev->vendor_id ||
+                         info.u.hid.dwProductId != This->sdldev->product_id))
+                    continue;
+
+                /* found device with same vid/pid, return this path. won't work
+                 * for multiple identical controllers... */
+
+                size = ARRAY_SIZE(pd->wszPath);
+                ur = GetRawInputDeviceInfoW(list[i].hDevice, RIDI_DEVICENAME, pd->wszPath, &size);
+                if (ur == (UINT)-1)
+                {
+                    HeapFree(GetProcessHeap(), 0, list);
+                    return DIERR_GENERIC;
+                }
+
+                pd->guidClass = GUID_DEVCLASS_HIDCLASS;
+
+                TRACE("DIPROP_GUIDANDPATH(%s, %s): returning path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
+                break;
+            }
+
+            HeapFree(GetProcessHeap(), 0, list);
+
+            if (i >= ndevs)
+            {
+                TRACE("couldn't find matching rawinput device\n");
+                return DIERR_GENERIC;
+            }
 
-            TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
             break;
         }
 
From f6b7dfe5f224bf13b5d00f706a15d5fec4267679 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 5 Sep 2019 10:31:35 -0500
Subject: [PATCH] dinput: Use axis ID to look up properties, not data offset

---
 dlls/dinput/joystick.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c
index b89a83500fe..06e733b9e5e 100644
--- a/dlls/dinput/joystick.c
+++ b/dlls/dinput/joystick.c
@@ -380,15 +380,15 @@ HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REF
                     remap_props.lMin = pr->lMin;
                     remap_props.lMax = pr->lMax;
 
-                    switch (This->base.data_format.wine_df->rgodf[i].dwOfs) {
-                    case DIJOFS_X        : This->js.lX  = joystick_map_axis(&remap_props, This->js.lX); break;
-                    case DIJOFS_Y        : This->js.lY  = joystick_map_axis(&remap_props, This->js.lY); break;
-                    case DIJOFS_Z        : This->js.lZ  = joystick_map_axis(&remap_props, This->js.lZ); break;
-                    case DIJOFS_RX       : This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
-                    case DIJOFS_RY       : This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
-                    case DIJOFS_RZ       : This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
-                    case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
-                    case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
+                    switch (DIDFT_GETINSTANCE(This->base.data_format.wine_df->rgodf[i].dwType)) {
+                    case 0: This->js.lX  = joystick_map_axis(&remap_props, This->js.lX); break;
+                    case 1: This->js.lY  = joystick_map_axis(&remap_props, This->js.lY); break;
+                    case 2: This->js.lZ  = joystick_map_axis(&remap_props, This->js.lZ); break;
+                    case 3: This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
+                    case 4: This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
+                    case 5: This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
+                    case 6: This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
+                    case 7: This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
 	            default: break;
                     }
 
@@ -410,15 +410,15 @@ HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REF
                     remap_props.lMin = pr->lMin;
                     remap_props.lMax = pr->lMax;
 
-                    switch (This->base.data_format.wine_df->rgodf[obj].dwOfs) {
-                    case DIJOFS_X        : This->js.lX  = joystick_map_axis(&remap_props, This->js.lX); break;
-                    case DIJOFS_Y        : This->js.lY  = joystick_map_axis(&remap_props, This->js.lY); break;
-                    case DIJOFS_Z        : This->js.lZ  = joystick_map_axis(&remap_props, This->js.lZ); break;
-                    case DIJOFS_RX       : This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
-                    case DIJOFS_RY       : This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
-                    case DIJOFS_RZ       : This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
-                    case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
-                    case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
+                    switch (DIDFT_GETINSTANCE(This->base.data_format.wine_df->rgodf[obj].dwType)) {
+                    case 0: This->js.lX  = joystick_map_axis(&remap_props, This->js.lX); break;
+                    case 1: This->js.lY  = joystick_map_axis(&remap_props, This->js.lY); break;
+                    case 2: This->js.lZ  = joystick_map_axis(&remap_props, This->js.lZ); break;
+                    case 3: This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
+                    case 4: This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
+                    case 5: This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
+                    case 6: This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
+                    case 7: This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
 		    default: break;
                     }
 
From 24a166c85891b8a78d4be2975928ae0135dd634c Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Wed, 4 Sep 2019 15:11:53 -0500
Subject: [PATCH] dinput: Fix DS4 object enumeration order

---
 dlls/dinput/joystick_sdl.c | 342 +++++++++++++++++++------------------
 1 file changed, 175 insertions(+), 167 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 219f7743826..581e47a25aa 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -90,7 +90,7 @@ HRESULT sdl_input_get_info_W(SDL_Joystick *dev, REFGUID rguid, LPDIEFFECTINFOW i
 
 struct device_state_item {
     int type;
-    int idx;
+    int id;
     int val;
 };
 
@@ -482,172 +482,177 @@ static int buttons_to_sdl_hat(int u, int r, int d, int l)
 /* playstation controllers */
 static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_state_item *st, int idx)
 {
-    static const int button_map_ds4_16button[] = {
-        /* [linux button] -> windows button */
-
-        /* [0] -> */ 1, /* cross */
-        /* [1] -> */ 2, /* circle */
-        /* [2] -> */ 0, /* square */
-        /* [3] -> */ 3, /* triangle */
-
-        /* [4] -> */ 8, /* share */
-        /* [5] -> */ 12, /* guide */
-        /* [6] -> */ 9, /* options */
-
-        /* [7] -> */ 10, /* L3 */
-        /* [8] -> */ 11, /* R3 */
-
-        /* [9] -> */ 4, /* L1 */
-        /* [10] -> */ 5, /* R1 */
+#define SPECIALCASE_HAT -1
+#define SPECIALCASE_L2_BUTTON -2
+#define SPECIALCASE_R2_BUTTON -3
+
+    static const struct {
+        int type;
+        int sdl_idx;
+        int dnp_id;
+    } map_ds4_16button[] = {
+        { ITEM_TYPE_AXIS, 3, 5 }, /* R2 */
+        { ITEM_TYPE_AXIS, 2, 2 }, /* L2 */
+        { ITEM_TYPE_AXIS, 1, 1 }, /* left vert */
+        { ITEM_TYPE_AXIS, 0, 0 }, /* left horiz */
+
+        { ITEM_TYPE_HAT, SPECIALCASE_HAT, 0 }, /* d-pad */
+
+        { ITEM_TYPE_BUTTON, 2, 0}, /* square */
+        { ITEM_TYPE_BUTTON, 0, 1}, /* cross */
+        { ITEM_TYPE_BUTTON, 1, 2}, /* circle */
+        { ITEM_TYPE_BUTTON, 3, 3}, /* triangle */
+
+        { ITEM_TYPE_BUTTON, 9, 4}, /* L1 */
+        { ITEM_TYPE_BUTTON, 10, 5}, /* R1 */
+        { ITEM_TYPE_BUTTON, SPECIALCASE_L2_BUTTON, 6}, /* L2 button */
+        { ITEM_TYPE_BUTTON, SPECIALCASE_R2_BUTTON, 7}, /* R2 button */
+        { ITEM_TYPE_BUTTON, 4, 8}, /* share */
+        { ITEM_TYPE_BUTTON, 6, 9}, /* options */
+
+        { ITEM_TYPE_BUTTON, 7, 10}, /* guide */
+        { ITEM_TYPE_BUTTON, 8, 11}, /* L3 */
+        { ITEM_TYPE_BUTTON, 5, 12}, /* R3 */
+
+        { ITEM_TYPE_BUTTON, 15, 13}, /* touchpad button */
+
+        { ITEM_TYPE_AXIS, 5, 4 }, /* right vert */
+        { ITEM_TYPE_AXIS, 4, 3 }, /* right horiz */
     };
 
-    static const int axis_map_ds4_16button[] = {
-        /* [linux axis] -> windows axis */
-
-        /* [0] -> */ 0, /* left horiz */
-        /* [1] -> */ 1, /* left vert */
-        /* [2] -> */ 2, /* right horiz */
-        /* [3] -> */ 5, /* right vert */
-        /* [4] -> */ 3, /* L2 */
-        /* [5] -> */ 4, /* R2 */
-    };
-
-    static const int DNP_TOUCHPAD_BUTTON = 13;
-    static const int SDL_TOUCHPAD_BUTTON = 15;
+    if(idx >= ARRAY_SIZE(map_ds4_16button))
+        return FALSE;
 
-    static const int DNP_L2_BUTTON = 6;
-    static const int SDL_L2_AXIS = 4;
+    st->type = map_ds4_16button[idx].type;
+    st->id = map_ds4_16button[idx].dnp_id;
 
-    static const int DNP_R2_BUTTON = 7;
-    static const int SDL_R2_AXIS = 5;
+    if(map_ds4_16button[idx].sdl_idx >= 0)
+    {
+        /* simple reads */
+        switch(map_ds4_16button[idx].type)
+        {
+        case ITEM_TYPE_BUTTON:
+            st->val = SDL_JoystickGetButton(This->device, map_ds4_16button[idx].sdl_idx);
+            return TRUE;
 
-    static const int SDL_DPAD_UP_BUTTON = 11;
-    static const int SDL_DPAD_DOWN_BUTTON = 12;
-    static const int SDL_DPAD_LEFT_BUTTON = 13;
-    static const int SDL_DPAD_RIGHT_BUTTON = 14;
+        case ITEM_TYPE_AXIS:
+            st->val = SDL_JoystickGetAxis(This->device, map_ds4_16button[idx].sdl_idx);
+            return TRUE;
 
-    if(idx < 11)
-    {
-        /* first 11 buttons */
-        st->type = ITEM_TYPE_BUTTON;
-        st->idx = button_map_ds4_16button[idx];
-        st->val = SDL_JoystickGetButton(This->device, idx);
-        return TRUE;
+        case ITEM_TYPE_HAT:
+            st->val = SDL_JoystickGetHat(This->device, map_ds4_16button[idx].sdl_idx);
+            return TRUE;
+        }
     }
 
-    if(idx < 17)
+    switch(map_ds4_16button[idx].sdl_idx){
+    case SPECIALCASE_HAT:
     {
-        /* six axes */
-        idx -= 11;
-        st->type = ITEM_TYPE_AXIS;
-        st->idx = axis_map_ds4_16button[idx];
-        st->val = SDL_JoystickGetAxis(This->device, idx);
+        /* d-pad */
+        static const int SDL_DPAD_UP_BUTTON = 11;
+        static const int SDL_DPAD_DOWN_BUTTON = 12;
+        static const int SDL_DPAD_LEFT_BUTTON = 13;
+        static const int SDL_DPAD_RIGHT_BUTTON = 14;
+        st->val = buttons_to_sdl_hat(
+                SDL_JoystickGetButton(This->device, SDL_DPAD_UP_BUTTON),
+                SDL_JoystickGetButton(This->device, SDL_DPAD_RIGHT_BUTTON),
+                SDL_JoystickGetButton(This->device, SDL_DPAD_DOWN_BUTTON),
+                SDL_JoystickGetButton(This->device, SDL_DPAD_LEFT_BUTTON));
         return TRUE;
     }
 
-    switch(idx)
+    case SPECIALCASE_L2_BUTTON :
     {
-    case 17:
-        /* touchpad button */
-        st->type = ITEM_TYPE_BUTTON;
-        st->idx = DNP_TOUCHPAD_BUTTON;
-        st->val = SDL_JoystickGetButton(This->device, SDL_TOUCHPAD_BUTTON);
-        return TRUE;
-
-    case 18:
         /* L2 button */
-        st->type = ITEM_TYPE_BUTTON;
-        st->idx = DNP_L2_BUTTON;
         /* turn button on at about 1/8 of the trigger travel */
+        static const int SDL_L2_AXIS = 4;
         st->val = SDL_JoystickGetAxis(This->device, SDL_L2_AXIS) > 3 * SDL_JOYSTICK_AXIS_MIN / 4;
         return TRUE;
+    }
 
-    case 19:
+    case SPECIALCASE_R2_BUTTON:
+    {
         /* R2 button */
-        st->type = ITEM_TYPE_BUTTON;
-        st->idx = DNP_R2_BUTTON;
         /* turn button on at about 1/8 of the trigger travel */
+        static const int SDL_R2_AXIS = 5;
         st->val = SDL_JoystickGetAxis(This->device, SDL_R2_AXIS) > 3 * SDL_JOYSTICK_AXIS_MIN / 4;
         return TRUE;
-
-    case 20:
-        /* dpad buttons --> hatswitch */
-        st->type = ITEM_TYPE_HAT;
-        st->idx = 0;
-        st->val = buttons_to_sdl_hat(
-                SDL_JoystickGetButton(This->device, SDL_DPAD_UP_BUTTON),
-                SDL_JoystickGetButton(This->device, SDL_DPAD_RIGHT_BUTTON),
-                SDL_JoystickGetButton(This->device, SDL_DPAD_DOWN_BUTTON),
-                SDL_JoystickGetButton(This->device, SDL_DPAD_LEFT_BUTTON));
-        return TRUE;
+    }
     }
 
+    ERR("???\n"); /* error in static data above */
     return FALSE;
+
+#undef SPECIALCASE_HAT
+#undef SPECIALCASE_L2_BUTTON
+#undef SPECIALCASE_R2_BUTTON
 }
 
 static BOOL enum_device_state_ds4_13button(JoystickImpl *This, struct device_state_item *st, int idx)
 {
-    static const int button_map_ds4_13button[] = {
-        /* [linux button] -> windows button */
-
-        /* [0] -> */ 1, /* cross */
-        /* [1] -> */ 2, /* circle */
-        /* [2] -> */ 3, /* triangle */
-        /* [3] -> */ 0, /* square */
-
-        /* [4] -> */ 4, /* L1 */
-        /* [5] -> */ 5, /* R1 */
-        /* [6] -> */ 6, /* L2 */
-        /* [7] -> */ 7, /* R2 */
-        /* [8] -> */ 8, /* share */
-        /* [9] -> */ 9, /* options */
-
-        /* [10] -> */ 12, /* guide */
-        /* [11] -> */ 10, /* L3 */
-        /* [12] -> */ 11, /* R3 */
+    static const struct {
+        int type;
+        int sdl_idx;
+        int dnp_id;
+    } map_ds4_13button[] = {
+        { ITEM_TYPE_AXIS, 4, 5 }, /* R2 */
+        { ITEM_TYPE_AXIS, 3, 2 }, /* L2 */
+        { ITEM_TYPE_AXIS, 1, 1 }, /* left vert */
+        { ITEM_TYPE_AXIS, 0, 0 }, /* left horiz */
+
+        { ITEM_TYPE_HAT, 0, 0 }, /* d-pad */
+
+        { ITEM_TYPE_BUTTON, 3, 0}, /* square */
+        { ITEM_TYPE_BUTTON, 0, 1}, /* cross */
+        { ITEM_TYPE_BUTTON, 1, 2}, /* circle */
+        { ITEM_TYPE_BUTTON, 2, 3}, /* triangle */
+
+        { ITEM_TYPE_BUTTON, 4, 4}, /* L1 */
+        { ITEM_TYPE_BUTTON, 5, 5}, /* R1 */
+        { ITEM_TYPE_BUTTON, 6, 6}, /* L2 button */
+        { ITEM_TYPE_BUTTON, 7, 7}, /* R2 button */
+        { ITEM_TYPE_BUTTON, 8, 8}, /* share */
+        { ITEM_TYPE_BUTTON, 9, 9}, /* options */
+
+        { ITEM_TYPE_BUTTON, 11, 10}, /* guide */
+        { ITEM_TYPE_BUTTON, 12, 11}, /* L3 */
+        { ITEM_TYPE_BUTTON, 10, 12}, /* R3 */
 
         /* ps4 controller through linux event API does not support touchpad button */
+        { ITEM_TYPE_BUTTON, -1, 13}, /* touchpad button */
+
+        { ITEM_TYPE_AXIS, 5, 4 }, /* right vert */
+        { ITEM_TYPE_AXIS, 2, 3 }, /* right horiz */
     };
 
-    static const int axis_map_ds4_13button[] = {
-        /* [linux axis] -> windows axis */
+    if(idx >= ARRAY_SIZE(map_ds4_13button))
+        return FALSE;
 
-        /* [0] -> */ 0, /* left horiz */
-        /* [1] -> */ 1, /* left vert */
-        /* [2] -> */ 3, /* L2 */
-        /* [3] -> */ 2, /* right horiz */
-        /* [4] -> */ 5, /* right vert */
-        /* [5] -> */ 4, /* R2 */
-    };
+    st->type = map_ds4_13button[idx].type;
+    st->id = map_ds4_13button[idx].dnp_id;
 
-    if(idx < This->sdldev->n_buttons)
+    if(map_ds4_13button[idx].sdl_idx < 0)
     {
-        st->type = ITEM_TYPE_BUTTON;
-        st->idx = button_map_ds4_13button[idx];
-        st->val = SDL_JoystickGetButton(This->device, idx);
+        st->val = 0;
         return TRUE;
     }
 
-    idx -= This->sdldev->n_buttons;
-
-    if(idx < This->sdldev->n_axes)
+    switch(map_ds4_13button[idx].type)
     {
-        st->type = ITEM_TYPE_AXIS;
-        st->idx = axis_map_ds4_13button[idx];
-        st->val = SDL_JoystickGetAxis(This->device, idx);
+    case ITEM_TYPE_BUTTON:
+        st->val = SDL_JoystickGetButton(This->device, map_ds4_13button[idx].sdl_idx);
         return TRUE;
-    }
 
-    idx -= This->sdldev->n_axes;
+    case ITEM_TYPE_AXIS:
+        st->val = SDL_JoystickGetAxis(This->device, map_ds4_13button[idx].sdl_idx);
+        return TRUE;
 
-    if(idx < This->sdldev->n_hats)
-    {
-        st->type = ITEM_TYPE_HAT;
-        st->idx = idx;
-        st->val = SDL_JoystickGetHat(This->device, idx);
+    case ITEM_TYPE_HAT:
+        st->val = SDL_JoystickGetHat(This->device, map_ds4_13button[idx].sdl_idx);
         return TRUE;
     }
 
+    ERR("???\n"); /* error in static data above */
     return FALSE;
 }
 
@@ -657,7 +662,7 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
     if(idx < This->sdldev->n_buttons)
     {
         st->type = ITEM_TYPE_BUTTON;
-        st->idx = idx;
+        st->id = idx;
         st->val = SDL_JoystickGetButton(This->device, idx);
         return TRUE;
     }
@@ -667,7 +672,7 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
     if(idx < This->sdldev->n_axes)
     {
         st->type = ITEM_TYPE_AXIS;
-        st->idx = idx;
+        st->id = idx;
         st->val = SDL_JoystickGetAxis(This->device, idx);
         return TRUE;
     }
@@ -677,7 +682,7 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
     if(idx < This->sdldev->n_hats)
     {
         st->type = ITEM_TYPE_HAT;
-        st->idx = idx;
+        st->id = idx;
         st->val = SDL_JoystickGetHat(This->device, idx);
         return TRUE;
     }
@@ -701,13 +706,13 @@ static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
         case ITEM_TYPE_BUTTON:
         {
             int val = item.val;
-            int oldVal = This->generic.js.rgbButtons[item.idx];
+            int oldVal = This->generic.js.rgbButtons[item.id];
             newVal = val ? 0x80 : 0x0;
-            This->generic.js.rgbButtons[item.idx] = newVal;
+            This->generic.js.rgbButtons[item.id] = newVal;
             if (oldVal != newVal)
             {
-                TRACE("Button: %i val %d oldVal %d newVal %d\n",  item.idx, val, oldVal, newVal);
-                inst_id = DIDFT_MAKEINSTANCE(item.idx) | DIDFT_PSHBUTTON;
+                TRACE("Button: %i val %d oldVal %d newVal %d\n",  item.id, val, oldVal, newVal);
+                inst_id = DIDFT_MAKEINSTANCE(item.id) | DIDFT_PSHBUTTON;
                 queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
             }
             break;
@@ -715,10 +720,13 @@ static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
 
         case ITEM_TYPE_AXIS:
         {
-            int oldVal;
+            int oldVal, obj;
+
+            obj = id_to_object(This->generic.base.data_format.wine_df, DIDFT_MAKEINSTANCE(item.id) | DIDFT_ABSAXIS);
             newVal = item.val;
-            newVal = joystick_map_axis(&This->generic.props[item.idx], newVal);
-            switch (item.idx)
+            newVal = joystick_map_axis(&This->generic.props[obj], newVal);
+
+            switch (item.id)
             {
                 case 0: oldVal = This->generic.js.lX;
                         This->generic.js.lX  = newVal; break;
@@ -739,8 +747,8 @@ static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
             }
             if (oldVal != newVal)
             {
-                TRACE("Axis: %i oldVal %d newVal %d\n",  item.idx, oldVal, newVal);
-                inst_id = DIDFT_MAKEINSTANCE(item.idx) | DIDFT_ABSAXIS;
+                TRACE("Axis: %i oldVal %d newVal %d\n",  item.id, oldVal, newVal);
+                inst_id = DIDFT_MAKEINSTANCE(item.id) | DIDFT_ABSAXIS;
                 queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
             }
             break;
@@ -748,7 +756,7 @@ static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
 
         case ITEM_TYPE_HAT:
         {
-            int oldVal = This->generic.js.rgdwPOV[item.idx];
+            int oldVal = This->generic.js.rgdwPOV[item.id];
             newVal = item.val;
             switch (newVal)
             {
@@ -764,9 +772,9 @@ static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
             }
             if (oldVal != newVal)
             {
-                TRACE("Hat : %i oldVal %d newVal %d\n",  item.idx, oldVal, newVal);
-                This->generic.js.rgdwPOV[item.idx] = newVal;
-                inst_id = DIDFT_MAKEINSTANCE(item.idx) | DIDFT_POV;
+                TRACE("Hat : %i oldVal %d newVal %d\n",  item.id, oldVal, newVal);
+                This->generic.js.rgdwPOV[item.id] = newVal;
+                inst_id = DIDFT_MAKEINSTANCE(item.id) | DIDFT_POV;
                 queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++);
             }
             break;
@@ -856,16 +864,6 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
         newDevice->generic.devcaps.dwAxes = 8;
     }
 
-    for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
-    {
-        newDevice->generic.props[i].lDevMin = -32768;
-        newDevice->generic.props[i].lDevMax = 32767;
-        newDevice->generic.props[i].lMin =  0;
-        newDevice->generic.props[i].lMax =  0xffff;
-        newDevice->generic.props[i].lDeadZone = 0;
-        newDevice->generic.props[i].lSaturation = 0;
-    }
-
     newDevice->generic.devcaps.dwPOVs = hat_count;
     if (newDevice->generic.devcaps.dwPOVs > 4)
     {
@@ -889,26 +887,36 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
 
-    for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
-    {
-        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[idx], df->dwObjSize);
-        df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(idx) | DIDFT_ABSAXIS;
-        if (newDevice->sdldev->has_ff && i < 2)
-             df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
-        ++idx;
-    }
-
-    for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
-    {
-        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
-        df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
-    }
-
-    for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
-    {
-        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
-        df->rgodf[idx].pguid = &GUID_Button;
-        df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
+    i = 0;
+    while(newDevice->enum_device_state(newDevice, &item, i++)){
+        switch(item.type){
+            case ITEM_TYPE_BUTTON:
+                memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[item.id + 12], df->dwObjSize);
+                df->rgodf[idx].pguid = &GUID_Button;
+                df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(item.id) | DIDFT_PSHBUTTON;
+                ++idx;
+                break;
+            case ITEM_TYPE_AXIS:
+                memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[item.id], df->dwObjSize);
+                df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(item.id) | DIDFT_ABSAXIS;
+                if (newDevice->sdldev->has_ff && i < 2)
+                     df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
+
+                newDevice->generic.props[idx].lDevMin = -32768;
+                newDevice->generic.props[idx].lDevMax = 32767;
+                newDevice->generic.props[idx].lMin =  0;
+                newDevice->generic.props[idx].lMax =  0xffff;
+                newDevice->generic.props[idx].lDeadZone = 0;
+                newDevice->generic.props[idx].lSaturation = 0;
+
+                ++idx;
+                break;
+            case ITEM_TYPE_HAT:
+                memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[item.id + 8], df->dwObjSize);
+                df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(item.id) | DIDFT_POV;
+                ++idx;
+                break;
+        }
     }
 
     if (newDevice->sdldev->has_ff)

From 33c20bdf86ab69ff34773d914f99eab5e88fe723 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 16 Sep 2019 14:32:02 -0500
Subject: [PATCH] dinput: Fix devices with too many objects

---
 dlls/dinput/joystick_sdl.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 581e47a25aa..cac7be30593 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -659,7 +659,11 @@ static BOOL enum_device_state_ds4_13button(JoystickImpl *This, struct device_sta
 /* straight 1:1 mapping of SDL items and dinput items */
 static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_item *st, int idx)
 {
-    if(idx < This->sdldev->n_buttons)
+    DWORD n_buttons, n_axes, n_hats;
+
+    n_buttons = This->generic.devcaps.dwButtons ? This->generic.devcaps.dwButtons : This->sdldev->n_buttons;
+
+    if(idx < n_buttons)
     {
         st->type = ITEM_TYPE_BUTTON;
         st->id = idx;
@@ -667,9 +671,11 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
         return TRUE;
     }
 
-    idx -= This->sdldev->n_buttons;
+    idx -= n_buttons;
 
-    if(idx < This->sdldev->n_axes)
+    n_axes = This->generic.devcaps.dwAxes ? This->generic.devcaps.dwAxes : This->sdldev->n_axes;
+
+    if(idx < n_axes)
     {
         st->type = ITEM_TYPE_AXIS;
         st->id = idx;
@@ -677,9 +683,11 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
         return TRUE;
     }
 
-    idx -= This->sdldev->n_axes;
+    idx -= n_axes;
+
+    n_hats = This->generic.devcaps.dwPOVs ? This->generic.devcaps.dwPOVs : This->sdldev->n_hats;
 
-    if(idx < This->sdldev->n_hats)
+    if(idx < n_hats)
     {
         st->type = ITEM_TYPE_HAT;
         st->id = idx;

From dc6c36904d8c26ea2a0a74d38f6bdad4eab96e9c Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Wed, 21 Aug 2019 14:12:27 -0500
Subject: [PATCH] dinput: Check for device loss in joystick Poll

---
 dlls/dinput/joystick.c            | 16 +++++++++-------
 dlls/dinput/joystick_linux.c      | 16 +++++++++-------
 dlls/dinput/joystick_linuxinput.c | 12 +++++++-----
 dlls/dinput/joystick_osx.c        | 20 +++++++++++---------
 dlls/dinput/joystick_private.h    |  2 +-
 dlls/dinput/joystick_sdl.c        |  7 ++++++-
 6 files changed, 43 insertions(+), 30 deletions(-)

diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c
index 06e733b9e5e..a3373c06cc5 100644
--- a/dlls/dinput/joystick.c
+++ b/dlls/dinput/joystick.c
@@ -793,8 +793,7 @@ HRESULT WINAPI JoystickWGenericImpl_Poll(LPDIRECTINPUTDEVICE8W iface)
         return DIERR_NOTACQUIRED;
     }
 
-    This->joy_polldev(IDirectInputDevice8A_from_impl(This));
-    return DI_OK;
+    return This->joy_polldev(IDirectInputDevice8A_from_impl(This));
 }
 
 HRESULT WINAPI JoystickAGenericImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
@@ -809,6 +808,7 @@ HRESULT WINAPI JoystickAGenericImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
   */
 HRESULT WINAPI JoystickWGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8W iface, DWORD len, LPVOID ptr)
 {
+    HRESULT hr;
     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
 
     TRACE("(%p,0x%08x,%p)\n", This, len, ptr);
@@ -819,12 +819,14 @@ HRESULT WINAPI JoystickWGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8W iface,
     }
 
     /* update joystick state */
-    This->joy_polldev(IDirectInputDevice8A_from_impl(This));
-
-    /* convert and copy data to user supplied buffer */
-    fill_DataFormat(ptr, len, &This->js, &This->base.data_format);
+    hr = This->joy_polldev(IDirectInputDevice8A_from_impl(This));
+    if (SUCCEEDED(hr))
+    {
+        /* convert and copy data to user supplied buffer */
+        fill_DataFormat(ptr, len, &This->js, &This->base.data_format);
+    }
 
-    return DI_OK;
+    return hr;
 }
 
 HRESULT WINAPI JoystickAGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8A iface, DWORD len, LPVOID ptr)
diff --git a/dlls/dinput/joystick_linux.c b/dlls/dinput/joystick_linux.c
index 7e01f90843b..24b8fac862a 100644
--- a/dlls/dinput/joystick_linux.c
+++ b/dlls/dinput/joystick_linux.c
@@ -143,7 +143,7 @@ static const GUID DInput_Wine_Joystick_Constant_Part_GUID = {
 static INT joystick_devices_count = -1;
 static struct JoyDev *joystick_devices;
 
-static void joy_polldev(LPDIRECTINPUTDEVICE8A iface);
+static HRESULT joy_polldev(LPDIRECTINPUTDEVICE8A iface);
 
 #define SYS_PATH_FORMAT "/sys/class/input/js%d/device/id/%s"
 static BOOL read_sys_id_variable(int index, const char *property, WORD *value)
@@ -845,7 +845,7 @@ static HRESULT WINAPI JoystickLinuxAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
     return JoystickLinuxWImpl_Unacquire(IDirectInputDevice8W_from_impl(This));
 }
 
-static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
+static HRESULT joy_polldev(LPDIRECTINPUTDEVICE8A iface)
 {
     struct pollfd plfd;
     struct js_event jse;
@@ -855,7 +855,7 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
 
     if (This->joyfd==-1) {
         WARN("no device\n");
-        return;
+        return DIERR_INPUTLOST;
     }
     while (1)
     {
@@ -865,16 +865,16 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
 	plfd.fd = This->joyfd;
 	plfd.events = POLLIN;
 	if (poll(&plfd,1,0) != 1)
-	    return;
+	    return DI_OK;
 	/* we have one event, so we can read */
 	if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
-	    return;
+	    return DIERR_INPUTLOST;
 	}
         TRACE("js_event: type 0x%x, number %d, value %d\n",
               jse.type,jse.number,jse.value);
         if (jse.type & JS_EVENT_BUTTON)
         {
-            if (jse.number >= This->generic.devcaps.dwButtons) return;
+            if (jse.number >= This->generic.devcaps.dwButtons) continue;
 
             inst_id = DIDFT_MAKEINSTANCE(jse.number) | DIDFT_PSHBUTTON;
             This->generic.js.rgbButtons[jse.number] = value = jse.value ? 0x80 : 0x00;
@@ -883,7 +883,7 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
         {
             int number = This->generic.axis_map[jse.number];	/* wine format object index */
 
-            if (number < 0) return;
+            if (number < 0) continue;
             inst_id = number < 8 ?  DIDFT_MAKEINSTANCE(number) | DIDFT_ABSAXIS :
                                     DIDFT_MAKEINSTANCE(number - 8) | DIDFT_POV;
             value = joystick_map_axis(&This->generic.props[id_to_object(This->generic.base.data_format.wine_df, inst_id)], jse.value);
@@ -918,6 +918,8 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
         if (inst_id >= 0)
             queue_event(iface, inst_id, value, GetCurrentTime(), This->generic.base.dinput->evsequence++);
     }
+
+    return DI_OK;
 }
 
 static const IDirectInputDevice8AVtbl JoystickAvt =
diff --git a/dlls/dinput/joystick_linuxinput.c b/dlls/dinput/joystick_linuxinput.c
index 1e8c542f21d..e65a6b281bf 100644
--- a/dlls/dinput/joystick_linuxinput.c
+++ b/dlls/dinput/joystick_linuxinput.c
@@ -155,7 +155,7 @@ static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl
 
 static void fake_current_js_state(JoystickImpl *ji);
 static void find_joydevs(void);
-static void joy_polldev(LPDIRECTINPUTDEVICE8A iface);
+static HRESULT joy_polldev(LPDIRECTINPUTDEVICE8A iface);
 
 /* This GUID is slightly different from the linux joystick one. Take note. */
 static const GUID DInput_Wine_Joystick_Base_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
@@ -826,14 +826,14 @@ static void fake_current_js_state(JoystickImpl *ji)
 #undef CENTER_AXIS
 
 /* convert wine format offset to user format object index */
-static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
+static HRESULT joy_polldev(LPDIRECTINPUTDEVICE8A iface)
 {
     struct pollfd plfd;
     struct input_event ie;
     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
 
     if (This->joyfd==-1)
-	return;
+	return DIERR_INPUTLOST;
 
     while (1)
     {
@@ -844,11 +844,11 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
 	plfd.events = POLLIN;
 
 	if (poll(&plfd,1,0) != 1)
-	    return;
+	    return DI_OK;
 
 	/* we have one event, so we can read */
 	if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
-	    return;
+	    return DIERR_INPUTLOST;
 
 	TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
 	switch (ie.type) {
@@ -927,6 +927,8 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
             queue_event(iface, inst_id,
                         value, GetCurrentTime(), This->generic.base.dinput->evsequence++);
     }
+
+    return DI_OK;
 }
 
 /******************************************************************************
diff --git a/dlls/dinput/joystick_osx.c b/dlls/dinput/joystick_osx.c
index 884dd7f3e19..9a0fb10a56d 100644
--- a/dlls/dinput/joystick_osx.c
+++ b/dlls/dinput/joystick_osx.c
@@ -767,7 +767,7 @@ static void get_osx_device_elements_props(JoystickImpl *device)
     }
 }
 
-static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
+static HRESULT poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
 {
     JoystickImpl *device = impl_from_IDirectInputDevice8A(iface);
     IOHIDElementRef device_main_element;
@@ -776,13 +776,13 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
     TRACE("device %p device->id %i\n", device, device->id);
 
     if (!device_main_elements || device->id >= CFArrayGetCount(device_main_elements))
-        return;
+        return DIERR_INPUTLOST;
 
     device_main_element = (IOHIDElementRef) CFArrayGetValueAtIndex(device_main_elements, device->id);
     hid_device = IOHIDElementGetDevice(device_main_element);
     TRACE("main element %s hid_device %s\n", debugstr_element(device_main_element), debugstr_device(hid_device));
     if (!hid_device)
-        return;
+        return DIERR_INPUTLOST;
 
     if (device->elements)
     {
@@ -809,9 +809,9 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
                     {
                         valueRef = NULL;
                         if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
-                            return;
+                            continue;
                         if (valueRef == NULL)
-                            return;
+                            continue;
                         val = IOHIDValueGetIntegerValue(valueRef);
                         newVal = val ? 0x80 : 0x0;
                         oldVal = device->generic.js.rgbButtons[button_idx];
@@ -835,9 +835,9 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
                             TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
                             valueRef = NULL;
                             if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
-                                return;
+                                continue;
                             if (valueRef == NULL)
-                                return;
+                                continue;
                             val = IOHIDValueGetIntegerValue(valueRef);
                             oldVal = device->generic.js.rgdwPOV[pov_idx];
                             if (val >= 8)
@@ -866,9 +866,9 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
 
                             valueRef = NULL;
                             if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
-                                return;
+                                continue;
                             if (valueRef == NULL)
-                                return;
+                                continue;
                             val = IOHIDValueGetIntegerValue(valueRef);
                             newVal = joystick_map_axis(&device->generic.props[idx], val);
                             switch (usage)
@@ -937,6 +937,8 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
             }
         }
     }
+
+    return DI_OK;
 }
 
 static INT find_joystick_devices(void)
diff --git a/dlls/dinput/joystick_private.h b/dlls/dinput/joystick_private.h
index b786c84decb..2af4a294303 100644
--- a/dlls/dinput/joystick_private.h
+++ b/dlls/dinput/joystick_private.h
@@ -33,7 +33,7 @@
 #define MAX_PROPS 164
 struct JoystickGenericImpl;
 
-typedef void joy_polldev_handler(LPDIRECTINPUTDEVICE8A iface);
+typedef HRESULT joy_polldev_handler(LPDIRECTINPUTDEVICE8A iface);
 
 typedef struct JoystickGenericImpl
 {
diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index cac7be30593..44e2a454651 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -698,7 +698,7 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
     return FALSE;
 }
 
-static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
+static HRESULT poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
 {
     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
     int i = 0;
@@ -706,6 +706,9 @@ static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
     int newVal = 0;
     struct device_state_item item;
 
+    if(!SDL_JoystickGetAttached(This->device))
+        return DIERR_INPUTLOST;
+
     SDL_JoystickUpdate();
 
     while(This->enum_device_state(This, &item, i++))
@@ -789,6 +792,8 @@ static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
         }
         }
     }
+
+    return DI_OK;
 }
 
 static enum_device_state_function select_enum_function(struct SDLDev *sdldev)

From df028719498646f518cbdc9a76b400ba34ed3f4e Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 12 Sep 2019 12:35:08 -0500
Subject: [PATCH] dinput: Keep track of joysticks even after disconnected

---
 dlls/dinput/joystick_sdl.c | 107 +++++++++++++++++++++----------------
 1 file changed, 61 insertions(+), 46 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 44e2a454651..98a2eb7b808 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -97,7 +97,9 @@ struct device_state_item {
 typedef BOOL (*enum_device_state_function)(JoystickImpl*, struct device_state_item *, int);
 
 struct SDLDev {
-    int id;
+    BOOL valid;
+
+    int instance_id;
     WORD vendor_id;
     WORD product_id;
     CHAR *name;
@@ -146,8 +148,16 @@ static const GUID DInput_PIDVID_Product_GUID = { /* PIDVID-0000-0000-0000-504944
   0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44}
 };
 
-static int have_sdldevs = -1;
-static struct SDLDev *sdldevs = NULL;
+static CRITICAL_SECTION sdldevs_lock;
+static CRITICAL_SECTION_DEBUG sdldevs_lock_debug =
+{
+    0, 0, &sdldevs_lock,
+    { &sdldevs_lock_debug.ProcessLocksList, &sdldevs_lock_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": sdldevs_lock") }
+};
+static CRITICAL_SECTION sdldevs_lock = { &sdldevs_lock_debug, -1, 0, 0, 0, 0 };
+
+static struct SDLDev sdldevs[64];
 
 /* logic from SDL2's SDL_ShouldIgnoreGameController */
 static BOOL is_in_sdl_blacklist(DWORD vid, DWORD pid)
@@ -184,6 +194,7 @@ static void find_sdldevs(void)
     Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick * joystick) = NULL;
     Uint16 (*pSDL_JoystickGetVendor)(SDL_Joystick * joystick) = NULL;
     void *sdl_handle = NULL;
+    static int have_sdldevs = -1;
 
     if (InterlockedCompareExchange(&have_sdldevs, 0, -1) != -1)
         /* Someone beat us to it */
@@ -204,25 +215,24 @@ static void find_sdldevs(void)
 
     for (i = 0; i < SDL_NumJoysticks(); i++)
     {
-        struct SDLDev sdldev = {0};
-        struct SDLDev *new_sdldevs;
+        struct SDLDev *sdldev = &sdldevs[have_sdldevs];
         SDL_Joystick *device;
         const CHAR* name;
 
-        sdldev.id = i;
         device = SDL_JoystickOpen(i);
+        sdldev->instance_id = SDL_JoystickInstanceID(device);
 
         name = SDL_JoystickName(device);
-        sdldev.name = HeapAlloc(GetProcessHeap(), 0, strlen(name) + 1);
-        strcpy(sdldev.name, name);
+        sdldev->name = HeapAlloc(GetProcessHeap(), 0, strlen(name) + 1);
+        strcpy(sdldev->name, name);
 
-        if (device_disabled_registry(sdldev.name)) {
+        if (device_disabled_registry(sdldev->name)) {
             SDL_JoystickClose(device);
-            HeapFree(GetProcessHeap(), 0, sdldev.name);
+            HeapFree(GetProcessHeap(), 0, sdldev->name);
             continue;
         }
 
-        TRACE("Found a joystick (%i) on %p: %s\n", have_sdldevs, device, sdldev.name);
+        TRACE("Found a joystick (%i) on %p: %s\n", have_sdldevs, device, sdldev->name);
 
         if (SDL_JoystickIsHaptic(device))
         {
@@ -230,56 +240,48 @@ static void find_sdldevs(void)
             if (haptic)
             {
                 TRACE(" ... with force feedback\n");
-                sdldev.has_ff = TRUE;
+                sdldev->has_ff = TRUE;
                 SDL_HapticClose(haptic);
             }
         }
 
         if(pSDL_JoystickGetVendor){
-            sdldev.vendor_id = pSDL_JoystickGetVendor(device);
-            sdldev.product_id = pSDL_JoystickGetProduct(device);
+            sdldev->vendor_id = pSDL_JoystickGetVendor(device);
+            sdldev->product_id = pSDL_JoystickGetProduct(device);
         }else{
-            sdldev.vendor_id = 0x01;
-            sdldev.product_id = SDL_JoystickInstanceID(device) + 1;
+            sdldev->vendor_id = 0x01;
+            sdldev->product_id = SDL_JoystickInstanceID(device) + 1;
         }
 
-        if(is_in_sdl_blacklist(sdldev.vendor_id, sdldev.product_id))
+        if(is_in_sdl_blacklist(sdldev->vendor_id, sdldev->product_id))
         {
-            TRACE("joystick %04x/%04x is in SDL blacklist, ignoring\n", sdldev.vendor_id, sdldev.product_id);
+            TRACE("joystick %04x/%04x is in SDL blacklist, ignoring\n", sdldev->vendor_id, sdldev->product_id);
             SDL_JoystickClose(device);
             continue;
         }
 
-        if(sdldev.vendor_id == VID_VALVE && sdldev.product_id == PID_VALVE_VIRTUAL_CONTROLLER)
+        if(sdldev->vendor_id == VID_VALVE && sdldev->product_id == PID_VALVE_VIRTUAL_CONTROLLER)
         {
-            sdldev.vendor_id = VID_MICROSOFT;
-            sdldev.product_id = PID_MICROSOFT_XBOX_360;
+            sdldev->vendor_id = VID_MICROSOFT;
+            sdldev->product_id = PID_MICROSOFT_XBOX_360;
         }
 
         {
             SDL_JoystickType type = SDL_JoystickGetType(device);
-            sdldev.is_joystick =
+            sdldev->is_joystick =
                 type == SDL_JOYSTICK_TYPE_WHEEL ||
                 type == SDL_JOYSTICK_TYPE_FLIGHT_STICK ||
                 type == SDL_JOYSTICK_TYPE_THROTTLE;
         }
 
-        sdldev.n_buttons = SDL_JoystickNumButtons(device);
-        sdldev.n_axes = SDL_JoystickNumAxes(device);
-        sdldev.n_hats = SDL_JoystickNumHats(device);
+        sdldev->n_buttons = SDL_JoystickNumButtons(device);
+        sdldev->n_axes = SDL_JoystickNumAxes(device);
+        sdldev->n_hats = SDL_JoystickNumHats(device);
 
-        if (!have_sdldevs)
-            new_sdldevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SDLDev));
-        else
-            new_sdldevs = HeapReAlloc(GetProcessHeap(), 0, sdldevs, (1 + have_sdldevs) * sizeof(struct SDLDev));
+        sdldev->valid = TRUE;
 
         SDL_JoystickClose(device);
-        if (!new_sdldevs)
-        {
-            continue;
-        }
-        sdldevs = new_sdldevs;
-        sdldevs[have_sdldevs] = sdldev;
+
         have_sdldevs++;
     }
 }
@@ -418,9 +420,8 @@ static HRESULT sdl_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
 {
   find_sdldevs();
 
-  if (id >= have_sdldevs) {
-    return E_FAIL;
-  }
+    if (id >= ARRAY_SIZE(sdldevs) || !sdldevs[id].valid)
+        return E_FAIL;
 
   if (!((dwDevType == 0) ||
         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
@@ -436,11 +437,13 @@ static HRESULT sdl_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
 
 static HRESULT sdl_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
 {
-  find_sdldevs();
+    find_sdldevs();
 
-  if (id >= have_sdldevs) {
-    return E_FAIL;
-  }
+    if (id >= ARRAY_SIZE(sdldevs) || !sdldevs[id].valid)
+        return E_FAIL;
+
+  if (!sdldevs[id].valid)
+      return E_FAIL;
 
   if (!((dwDevType == 0) ||
         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
@@ -826,6 +829,17 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     DIDEVICEINSTANCEW ddi;
     int i,idx = 0, axis_count = 0, button_count = 0, hat_count = 0;
     struct device_state_item item;
+    SDL_Joystick *sdl_js;
+
+    sdl_js = SDL_JoystickFromInstanceID(sdldevs[index].instance_id);
+    if (!sdl_js)
+        return NULL;
+
+    if (!SDL_JoystickGetAttached(sdl_js))
+    {
+        SDL_JoystickClose(sdl_js);
+        return NULL;
+    }
 
     newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl));
     if (!newDevice) return NULL;
@@ -852,7 +866,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
 
     /* Open Device */
-    newDevice->device = SDL_JoystickOpen(newDevice->sdldev->id);
+    newDevice->device = sdl_js;
     newDevice->haptic = SDL_HapticOpenFromJoystick(newDevice->device);
 
     i = 0;
@@ -992,8 +1006,7 @@ static HRESULT sdl_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID
     find_sdldevs();
     *pdev = NULL;
 
-    if ((index = get_joystick_index(rguid)) < 0xffff &&
-        have_sdldevs && index < have_sdldevs)
+    if ((index = get_joystick_index(rguid)) < 0xffff && sdldevs[index].valid)
     {
         JoystickImpl *This;
 
@@ -1020,6 +1033,8 @@ static HRESULT sdl_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID
         }
 
         This = alloc_device(rguid, dinput, index);
+        if (!This)
+            return DIERR_INPUTLOST;
         TRACE("Created a Joystick device (%p)\n", This);
 
         if (!This) return DIERR_OUTOFMEMORY;
@@ -1106,7 +1121,7 @@ static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF
         {
             LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
 
-            pd->dwData = This->sdldev->id;
+            pd->dwData = This->sdldev - sdldevs;
             TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData);
             break;
         }

From 16069034cacff363fe6ee0d8774f6c86932c3695 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Thu, 12 Sep 2019 14:21:08 -0500
Subject: [PATCH] dinput: Poll for new SDL devices when requested

---
 dlls/dinput/joystick_sdl.c | 136 ++++++++++++++++++++++++-------------
 1 file changed, 90 insertions(+), 46 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 98a2eb7b808..a1680d7265d 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -109,6 +109,9 @@ struct SDLDev {
     BOOL has_ff, is_joystick;
     int autocenter;
     int gain;
+
+    SDL_Joystick *sdl_js;
+
     struct list effects;
 };
 
@@ -188,20 +191,12 @@ static BOOL is_in_sdl_blacklist(DWORD vid, DWORD pid)
     return strcasestr(blacklist, needle) != NULL;
 }
 
-static void find_sdldevs(void)
+static Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick *);
+static Uint16 (*pSDL_JoystickGetVendor)(SDL_Joystick *);
+
+static BOOL WINAPI sdldrv_init(INIT_ONCE *once, void *param, void **context)
 {
-    int i;
-    Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick * joystick) = NULL;
-    Uint16 (*pSDL_JoystickGetVendor)(SDL_Joystick * joystick) = NULL;
     void *sdl_handle = NULL;
-    static int have_sdldevs = -1;
-
-    if (InterlockedCompareExchange(&have_sdldevs, 0, -1) != -1)
-        /* Someone beat us to it */
-        return;
-
-    SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_HAPTIC);
-    SDL_JoystickEventState(SDL_ENABLE);
 
     sdl_handle = wine_dlopen(SONAME_LIBSDL2, RTLD_NOW, NULL, 0);
     if (sdl_handle) {
@@ -213,12 +208,59 @@ static void find_sdldevs(void)
         ERR("SDL installation is old! Please upgrade to >=2.0.6 to get accurate joystick information.\n");
     }
 
+    SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_HAPTIC);
+    SDL_JoystickEventState(SDL_ENABLE);
+
+    return TRUE;
+}
+
+static void find_sdldevs(void)
+{
+    static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
+    static ULONGLONG last_check = 0;
+    ULONGLONG now;
+    int i;
+
+    InitOnceExecuteOnce(&init_once, sdldrv_init, NULL, NULL);
+
+    SDL_PumpEvents();
+
+    now = GetTickCount64();
+
+    if(last_check > 0 && last_check + 1000 > now)
+        return;
+
+    last_check = now;
+
+    EnterCriticalSection(&sdldevs_lock);
+
     for (i = 0; i < SDL_NumJoysticks(); i++)
     {
-        struct SDLDev *sdldev = &sdldevs[have_sdldevs];
+        struct SDLDev *sdldev = &sdldevs[0];
         SDL_Joystick *device;
         const CHAR* name;
 
+        while(sdldev < &sdldevs[ARRAY_SIZE(sdldevs)] &&
+                sdldev->valid)
+        {
+            if(sdldev->instance_id == SDL_JoystickGetDeviceInstanceID(i))
+                break;
+            sdldev++;
+        }
+
+        if(sdldev >= &sdldevs[ARRAY_SIZE(sdldevs)])
+        {
+            ERR("ran out of joystick slots!!\n");
+            LeaveCriticalSection(&sdldevs_lock);
+            return;
+        }
+
+        if(sdldev->valid)
+        {
+            /* this joystic is already discovered */
+            continue;
+        }
+
         device = SDL_JoystickOpen(i);
         sdldev->instance_id = SDL_JoystickInstanceID(device);
 
@@ -232,7 +274,7 @@ static void find_sdldevs(void)
             continue;
         }
 
-        TRACE("Found a joystick (%i) on %p: %s\n", have_sdldevs, device, sdldev->name);
+        TRACE("Found a joystick on %p: %s\n", device, sdldev->name);
 
         if (SDL_JoystickIsHaptic(device))
         {
@@ -257,6 +299,7 @@ static void find_sdldevs(void)
         {
             TRACE("joystick %04x/%04x is in SDL blacklist, ignoring\n", sdldev->vendor_id, sdldev->product_id);
             SDL_JoystickClose(device);
+            HeapFree(GetProcessHeap(), 0, sdldev->name);
             continue;
         }
 
@@ -278,12 +321,13 @@ static void find_sdldevs(void)
         sdldev->n_axes = SDL_JoystickNumAxes(device);
         sdldev->n_hats = SDL_JoystickNumHats(device);
 
-        sdldev->valid = TRUE;
-
-        SDL_JoystickClose(device);
+        sdldev->sdl_js = device;
 
-        have_sdldevs++;
+        /* must be last member to be set */
+        sdldev->valid = TRUE;
     }
+
+    LeaveCriticalSection(&sdldevs_lock);
 }
 
 static struct device_info_override {
@@ -418,21 +462,27 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
 
 static HRESULT sdl_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
 {
-  find_sdldevs();
+    find_sdldevs();
 
     if (id >= ARRAY_SIZE(sdldevs) || !sdldevs[id].valid)
         return E_FAIL;
 
-  if (!((dwDevType == 0) ||
-        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
-        (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
-    return S_FALSE;
+    if (!((dwDevType == 0) ||
+          ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
+          (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
+        return S_FALSE;
+
+    if ((dwFlags & DIEDFL_FORCEFEEDBACK) && !sdldevs[id].has_ff)
+        return S_FALSE;
+
+    if (dwFlags & DIEDFL_ATTACHEDONLY)
+    {
+        if (!SDL_JoystickGetAttached(sdldevs[id].sdl_js))
+            return S_FALSE;
+    }
 
-  if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || sdldevs[id].has_ff) {
     fill_joystick_dideviceinstanceA(lpddi, version, id);
     return S_OK;
-  }
-  return S_FALSE;
 }
 
 static HRESULT sdl_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
@@ -442,19 +492,22 @@ static HRESULT sdl_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
     if (id >= ARRAY_SIZE(sdldevs) || !sdldevs[id].valid)
         return E_FAIL;
 
-  if (!sdldevs[id].valid)
-      return E_FAIL;
+    if (!((dwDevType == 0) ||
+          ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
+          (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
+        return S_FALSE;
+
+    if ((dwFlags & DIEDFL_FORCEFEEDBACK) && !sdldevs[id].has_ff)
+        return S_FALSE;
 
-  if (!((dwDevType == 0) ||
-        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
-        (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
-    return S_FALSE;
+    if (dwFlags & DIEDFL_ATTACHEDONLY)
+    {
+        if (!SDL_JoystickGetAttached(sdldevs[id].sdl_js))
+            return S_FALSE;
+    }
 
-  if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || sdldevs[id].has_ff) {
     fill_joystick_dideviceinstanceW(lpddi, version, id);
     return S_OK;
-  }
-  return S_FALSE;
 }
 
 static int buttons_to_sdl_hat(int u, int r, int d, int l)
@@ -829,18 +882,10 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     DIDEVICEINSTANCEW ddi;
     int i,idx = 0, axis_count = 0, button_count = 0, hat_count = 0;
     struct device_state_item item;
-    SDL_Joystick *sdl_js;
 
-    sdl_js = SDL_JoystickFromInstanceID(sdldevs[index].instance_id);
-    if (!sdl_js)
+    if (!SDL_JoystickGetAttached(sdldevs[index].sdl_js))
         return NULL;
 
-    if (!SDL_JoystickGetAttached(sdl_js))
-    {
-        SDL_JoystickClose(sdl_js);
-        return NULL;
-    }
-
     newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl));
     if (!newDevice) return NULL;
 
@@ -866,7 +911,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
 
     /* Open Device */
-    newDevice->device = sdl_js;
+    newDevice->device = sdldevs[index].sdl_js;
     newDevice->haptic = SDL_HapticOpenFromJoystick(newDevice->device);
 
     i = 0;
@@ -1066,7 +1111,6 @@ static ULONG WINAPI JoystickWImpl_Release(LPDIRECTINPUTDEVICE8W iface)
         TRACE("Closing Joystick: %p\n",This);
         if (This->sdldev->has_ff)
             SDL_HapticClose(This->haptic);
-        SDL_JoystickClose(This->device);
         This->device = NULL;
     }
     return IDirectInputDevice2WImpl_Release(iface);

From 9fe580cc20e2b91acbef8ed47e89c9246d405645 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 16 Sep 2019 12:31:37 -0500
Subject: [PATCH] dinput: Access SDL_Joystick member once per operation

In case it changes out from under us.
---
 dlls/dinput/joystick_sdl.c | 103 +++++++++++++++++--------------------
 1 file changed, 48 insertions(+), 55 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index a1680d7265d..059f048300e 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -94,7 +94,7 @@ struct device_state_item {
     int val;
 };
 
-typedef BOOL (*enum_device_state_function)(JoystickImpl*, struct device_state_item *, int);
+typedef BOOL (*enum_device_state_function)(SDL_Joystick *, JoystickImpl *, struct device_state_item *, int);
 
 struct SDLDev {
     BOOL valid;
@@ -106,11 +106,12 @@ struct SDLDev {
 
     int n_buttons, n_axes, n_hats;
 
-    BOOL has_ff, is_joystick;
+    BOOL is_joystick;
     int autocenter;
     int gain;
 
     SDL_Joystick *sdl_js;
+    SDL_Haptic *sdl_haptic;
 
     struct list effects;
 };
@@ -120,8 +121,6 @@ struct JoystickImpl
     struct JoystickGenericImpl generic;
     struct SDLDev              *sdldev;
 
-    SDL_Joystick *device;
-    SDL_Haptic *haptic;
     BOOL ff_paused;
 
     enum_device_state_function enum_device_state;
@@ -238,6 +237,7 @@ static void find_sdldevs(void)
     {
         struct SDLDev *sdldev = &sdldevs[0];
         SDL_Joystick *device;
+        SDL_Haptic *haptic = NULL;
         const CHAR* name;
 
         while(sdldev < &sdldevs[ARRAY_SIZE(sdldevs)] &&
@@ -278,12 +278,10 @@ static void find_sdldevs(void)
 
         if (SDL_JoystickIsHaptic(device))
         {
-            SDL_Haptic *haptic = SDL_HapticOpenFromJoystick(device);
+            haptic = SDL_HapticOpenFromJoystick(device);
             if (haptic)
             {
                 TRACE(" ... with force feedback\n");
-                sdldev->has_ff = TRUE;
-                SDL_HapticClose(haptic);
             }
         }
 
@@ -322,6 +320,7 @@ static void find_sdldevs(void)
         sdldev->n_hats = SDL_JoystickNumHats(device);
 
         sdldev->sdl_js = device;
+        sdldev->sdl_haptic = haptic;
 
         /* must be last member to be set */
         sdldev->valid = TRUE;
@@ -472,7 +471,7 @@ static HRESULT sdl_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
           (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
         return S_FALSE;
 
-    if ((dwFlags & DIEDFL_FORCEFEEDBACK) && !sdldevs[id].has_ff)
+    if ((dwFlags & DIEDFL_FORCEFEEDBACK) && !sdldevs[id].sdl_haptic)
         return S_FALSE;
 
     if (dwFlags & DIEDFL_ATTACHEDONLY)
@@ -497,7 +496,7 @@ static HRESULT sdl_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
           (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
         return S_FALSE;
 
-    if ((dwFlags & DIEDFL_FORCEFEEDBACK) && !sdldevs[id].has_ff)
+    if ((dwFlags & DIEDFL_FORCEFEEDBACK) && !sdldevs[id].sdl_haptic)
         return S_FALSE;
 
     if (dwFlags & DIEDFL_ATTACHEDONLY)
@@ -536,7 +535,7 @@ static int buttons_to_sdl_hat(int u, int r, int d, int l)
 }
 
 /* playstation controllers */
-static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_state_item *st, int idx)
+static BOOL enum_device_state_ds4_16button(SDL_Joystick *js, JoystickImpl *This, struct device_state_item *st, int idx)
 {
 #define SPECIALCASE_HAT -1
 #define SPECIALCASE_L2_BUTTON -2
@@ -588,15 +587,15 @@ static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_sta
         switch(map_ds4_16button[idx].type)
         {
         case ITEM_TYPE_BUTTON:
-            st->val = SDL_JoystickGetButton(This->device, map_ds4_16button[idx].sdl_idx);
+            st->val = SDL_JoystickGetButton(js, map_ds4_16button[idx].sdl_idx);
             return TRUE;
 
         case ITEM_TYPE_AXIS:
-            st->val = SDL_JoystickGetAxis(This->device, map_ds4_16button[idx].sdl_idx);
+            st->val = SDL_JoystickGetAxis(js, map_ds4_16button[idx].sdl_idx);
             return TRUE;
 
         case ITEM_TYPE_HAT:
-            st->val = SDL_JoystickGetHat(This->device, map_ds4_16button[idx].sdl_idx);
+            st->val = SDL_JoystickGetHat(js, map_ds4_16button[idx].sdl_idx);
             return TRUE;
         }
     }
@@ -610,10 +609,10 @@ static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_sta
         static const int SDL_DPAD_LEFT_BUTTON = 13;
         static const int SDL_DPAD_RIGHT_BUTTON = 14;
         st->val = buttons_to_sdl_hat(
-                SDL_JoystickGetButton(This->device, SDL_DPAD_UP_BUTTON),
-                SDL_JoystickGetButton(This->device, SDL_DPAD_RIGHT_BUTTON),
-                SDL_JoystickGetButton(This->device, SDL_DPAD_DOWN_BUTTON),
-                SDL_JoystickGetButton(This->device, SDL_DPAD_LEFT_BUTTON));
+                SDL_JoystickGetButton(js, SDL_DPAD_UP_BUTTON),
+                SDL_JoystickGetButton(js, SDL_DPAD_RIGHT_BUTTON),
+                SDL_JoystickGetButton(js, SDL_DPAD_DOWN_BUTTON),
+                SDL_JoystickGetButton(js, SDL_DPAD_LEFT_BUTTON));
         return TRUE;
     }
 
@@ -622,7 +621,7 @@ static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_sta
         /* L2 button */
         /* turn button on at about 1/8 of the trigger travel */
         static const int SDL_L2_AXIS = 4;
-        st->val = SDL_JoystickGetAxis(This->device, SDL_L2_AXIS) > 3 * SDL_JOYSTICK_AXIS_MIN / 4;
+        st->val = SDL_JoystickGetAxis(js, SDL_L2_AXIS) > 3 * SDL_JOYSTICK_AXIS_MIN / 4;
         return TRUE;
     }
 
@@ -631,7 +630,7 @@ static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_sta
         /* R2 button */
         /* turn button on at about 1/8 of the trigger travel */
         static const int SDL_R2_AXIS = 5;
-        st->val = SDL_JoystickGetAxis(This->device, SDL_R2_AXIS) > 3 * SDL_JOYSTICK_AXIS_MIN / 4;
+        st->val = SDL_JoystickGetAxis(js, SDL_R2_AXIS) > 3 * SDL_JOYSTICK_AXIS_MIN / 4;
         return TRUE;
     }
     }
@@ -644,7 +643,7 @@ static BOOL enum_device_state_ds4_16button(JoystickImpl *This, struct device_sta
 #undef SPECIALCASE_R2_BUTTON
 }
 
-static BOOL enum_device_state_ds4_13button(JoystickImpl *This, struct device_state_item *st, int idx)
+static BOOL enum_device_state_ds4_13button(SDL_Joystick *js, JoystickImpl *This, struct device_state_item *st, int idx)
 {
     static const struct {
         int type;
@@ -696,15 +695,15 @@ static BOOL enum_device_state_ds4_13button(JoystickImpl *This, struct device_sta
     switch(map_ds4_13button[idx].type)
     {
     case ITEM_TYPE_BUTTON:
-        st->val = SDL_JoystickGetButton(This->device, map_ds4_13button[idx].sdl_idx);
+        st->val = SDL_JoystickGetButton(js, map_ds4_13button[idx].sdl_idx);
         return TRUE;
 
     case ITEM_TYPE_AXIS:
-        st->val = SDL_JoystickGetAxis(This->device, map_ds4_13button[idx].sdl_idx);
+        st->val = SDL_JoystickGetAxis(js, map_ds4_13button[idx].sdl_idx);
         return TRUE;
 
     case ITEM_TYPE_HAT:
-        st->val = SDL_JoystickGetHat(This->device, map_ds4_13button[idx].sdl_idx);
+        st->val = SDL_JoystickGetHat(js, map_ds4_13button[idx].sdl_idx);
         return TRUE;
     }
 
@@ -713,7 +712,7 @@ static BOOL enum_device_state_ds4_13button(JoystickImpl *This, struct device_sta
 }
 
 /* straight 1:1 mapping of SDL items and dinput items */
-static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_item *st, int idx)
+static BOOL enum_device_state_standard(SDL_Joystick *js, JoystickImpl *This, struct device_state_item *st, int idx)
 {
     DWORD n_buttons, n_axes, n_hats;
 
@@ -723,7 +722,7 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
     {
         st->type = ITEM_TYPE_BUTTON;
         st->id = idx;
-        st->val = SDL_JoystickGetButton(This->device, idx);
+        st->val = SDL_JoystickGetButton(js, idx);
         return TRUE;
     }
 
@@ -735,7 +734,7 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
     {
         st->type = ITEM_TYPE_AXIS;
         st->id = idx;
-        st->val = SDL_JoystickGetAxis(This->device, idx);
+        st->val = SDL_JoystickGetAxis(js, idx);
         return TRUE;
     }
 
@@ -747,7 +746,7 @@ static BOOL enum_device_state_standard(JoystickImpl *This, struct device_state_i
     {
         st->type = ITEM_TYPE_HAT;
         st->id = idx;
-        st->val = SDL_JoystickGetHat(This->device, idx);
+        st->val = SDL_JoystickGetHat(js, idx);
         return TRUE;
     }
 
@@ -761,13 +760,14 @@ static HRESULT poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
     int inst_id = 0;
     int newVal = 0;
     struct device_state_item item;
+    SDL_Joystick *js = This->sdldev->sdl_js;
 
-    if(!SDL_JoystickGetAttached(This->device))
+    if(!SDL_JoystickGetAttached(js))
         return DIERR_INPUTLOST;
 
     SDL_JoystickUpdate();
 
-    while(This->enum_device_state(This, &item, i++))
+    while(This->enum_device_state(js, This, &item, i++))
     {
         switch(item.type){
         case ITEM_TYPE_BUTTON:
@@ -882,8 +882,11 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     DIDEVICEINSTANCEW ddi;
     int i,idx = 0, axis_count = 0, button_count = 0, hat_count = 0;
     struct device_state_item item;
+    SDL_Joystick *js;
 
-    if (!SDL_JoystickGetAttached(sdldevs[index].sdl_js))
+    js = sdldevs[index].sdl_js;
+
+    if (!SDL_JoystickGetAttached(js))
         return NULL;
 
     newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl));
@@ -911,11 +914,9 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
 
     /* Open Device */
-    newDevice->device = sdldevs[index].sdl_js;
-    newDevice->haptic = SDL_HapticOpenFromJoystick(newDevice->device);
 
     i = 0;
-    while(newDevice->enum_device_state(newDevice, &item, i++)){
+    while(newDevice->enum_device_state(js, newDevice, &item, i++)){
         switch(item.type){
             case ITEM_TYPE_BUTTON:
                 ++button_count;
@@ -960,7 +961,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
 
     i = 0;
-    while(newDevice->enum_device_state(newDevice, &item, i++)){
+    while(newDevice->enum_device_state(js, newDevice, &item, i++)){
         switch(item.type){
             case ITEM_TYPE_BUTTON:
                 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[item.id + 12], df->dwObjSize);
@@ -971,7 +972,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
             case ITEM_TYPE_AXIS:
                 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[item.id], df->dwObjSize);
                 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(item.id) | DIDFT_ABSAXIS;
-                if (newDevice->sdldev->has_ff && i < 2)
+                if (newDevice->sdldev->sdl_haptic && i < 2)
                      df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
 
                 newDevice->generic.props[idx].lDevMin = -32768;
@@ -991,7 +992,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
         }
     }
 
-    if (newDevice->sdldev->has_ff)
+    if (newDevice->sdldev->sdl_haptic)
         newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
 
     newDevice->generic.base.data_format.wine_df = df;
@@ -1004,7 +1005,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     fill_joystick_dideviceinstanceW(&ddi, newDevice->generic.base.dinput->dwVersion, index);
     newDevice->generic.devcaps.dwDevType = ddi.dwDevType;
 
-    if (newDevice->sdldev->has_ff)
+    if (newDevice->sdldev->sdl_haptic)
         newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
 
     IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
@@ -1104,15 +1105,7 @@ const struct dinput_device joystick_sdl_device = {
 
 static ULONG WINAPI JoystickWImpl_Release(LPDIRECTINPUTDEVICE8W iface)
 {
-    JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
     TRACE("(this=%p)\n", iface);
-    if (This->generic.base.ref == 1 && This->device >= 0)
-    {
-        TRACE("Closing Joystick: %p\n",This);
-        if (This->sdldev->has_ff)
-            SDL_HapticClose(This->haptic);
-        This->device = NULL;
-    }
     return IDirectInputDevice2WImpl_Release(iface);
 }
 
@@ -1266,7 +1259,7 @@ static BOOL _SetProperty(JoystickImpl *This, const GUID *prop, const DIPROPHEADE
 
             This->sdldev->autocenter = pd->dwData == DIPROPAUTOCENTER_ON;
 
-            rc = SDL_HapticSetAutocenter(This->haptic, This->sdldev->autocenter * 100);
+            rc = SDL_HapticSetAutocenter(This->sdldev->sdl_haptic, This->sdldev->autocenter * 100);
             if (rc != 0)
                 ERR("SDL_HapticSetAutocenter failed: %s\n", SDL_GetError());
             break;
@@ -1280,7 +1273,7 @@ static BOOL _SetProperty(JoystickImpl *This, const GUID *prop, const DIPROPHEADE
 
             This->sdldev->gain = pd->dwData;
 
-            rc = SDL_HapticSetGain(This->haptic, sdl_gain);
+            rc = SDL_HapticSetGain(This->sdldev->sdl_haptic, sdl_gain);
             if (rc != 0)
                 ERR("SDL_HapticSetGain (%i -> %i) failed: %s\n", pd->dwData, sdl_gain, SDL_GetError());
             break;
@@ -1366,7 +1359,7 @@ static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
     TRACE("%p %s %p %p %p\n", iface, debugstr_guid(rguid), lpeff, ppdef, pUnkOuter);
     if (lpeff) dump_DIEFFECT(lpeff, rguid, 0);
 
-    if(!This->sdldev->has_ff){
+    if(!This->sdldev->sdl_haptic){
         TRACE("No force feedback support\n");
         *ppdef = NULL;
         return DIERR_UNSUPPORTED;
@@ -1384,7 +1377,7 @@ static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
     if (!(new_effect = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_effect))))
     return DIERR_OUTOFMEMORY;
 
-    retval = sdl_create_effect(This->haptic, rguid, &new_effect->entry, &new_effect->ref);
+    retval = sdl_create_effect(This->sdldev->sdl_haptic, rguid, &new_effect->entry, &new_effect->ref);
     if (retval != DI_OK)
     {
         HeapFree(GetProcessHeap(), 0, new_effect);
@@ -1435,7 +1428,7 @@ static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
     TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type);
 
     dei.dwSize = sizeof(DIEFFECTINFOW);
-    query = SDL_HapticQuery(This->haptic);
+    query = SDL_HapticQuery(This->sdldev->sdl_haptic);
     TRACE("Effects 0x%x\n",query);
 
     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
@@ -1516,7 +1509,7 @@ static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
     TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type);
 
     dei.dwSize = sizeof(DIEFFECTINFOA);
-    query = SDL_HapticQuery(This->haptic);
+    query = SDL_HapticQuery(This->sdldev->sdl_haptic);
     TRACE("Effects 0x%x\n",query);
 
     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
@@ -1590,7 +1583,7 @@ static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
 {
     JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
-    return sdl_input_get_info_W(This->device, guid, pdei);
+    return sdl_input_get_info_W(This->sdldev->sdl_js, guid, pdei);
 }
 
 static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
@@ -1599,7 +1592,7 @@ static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
 {
     JoystickImpl* This = impl_from_IDirectInputDevice8A(iface);
     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
-    return sdl_input_get_info_A(This->device, guid, pdei);
+    return sdl_input_get_info_A(This->sdldev->sdl_js, guid, pdei);
 }
 
 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8W iface, DWORD dwFlags)
@@ -1633,12 +1626,12 @@ static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE
     }
     case DISFFC_PAUSE:
         This->ff_paused = TRUE;
-        if (SDL_HapticPause(This->haptic) != 0)
+        if (SDL_HapticPause(This->sdldev->sdl_haptic) != 0)
             ERR("SDL_HapticPause failed: %s\n",SDL_GetError());
         break;
     case DISFFC_CONTINUE:
         This->ff_paused = FALSE;
-        if (SDL_HapticUnpause(This->haptic) != 0)
+        if (SDL_HapticUnpause(This->sdldev->sdl_haptic) != 0)
             ERR("SDL_HapticUnpause failed: %s\n",SDL_GetError());
         break;
 

From a762c30a3019470b3b84e74eca25797cb0771197 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 16 Sep 2019 13:49:06 -0500
Subject: [PATCH] dinput: Check for new devices on every access

---
 dlls/dinput/joystick_sdl.c | 36 +++++++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 059f048300e..324e8679601 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -102,6 +102,7 @@ struct SDLDev {
     int instance_id;
     WORD vendor_id;
     WORD product_id;
+    SDL_JoystickGUID sdl_guid;
     CHAR *name;
 
     int n_buttons, n_axes, n_hats;
@@ -243,8 +244,15 @@ static void find_sdldevs(void)
         while(sdldev < &sdldevs[ARRAY_SIZE(sdldevs)] &&
                 sdldev->valid)
         {
+            SDL_JoystickGUID sdl_guid;
             if(sdldev->instance_id == SDL_JoystickGetDeviceInstanceID(i))
                 break;
+            sdl_guid = SDL_JoystickGetDeviceGUID(i);
+            if(!memcmp(&sdldev->sdl_guid, &sdl_guid, sizeof(SDL_JoystickGUID))){
+                if(!SDL_JoystickGetAttached(sdldev->sdl_js))
+                    /* same GUID but detached; reconnected, so assign to this slot */
+                    break;
+            }
             sdldev++;
         }
 
@@ -257,12 +265,26 @@ static void find_sdldevs(void)
 
         if(sdldev->valid)
         {
-            /* this joystic is already discovered */
+            if(SDL_JoystickGetAttached(sdldev->sdl_js))
+            {
+                /* this joystic is already discovered */
+                continue;
+            }
+
+            /* reconnected, update sdldev */
+            TRACE("reconnected \"%s\"\n", sdldev->name);
+            device = SDL_JoystickOpen(i);
+            sdldev->instance_id = SDL_JoystickInstanceID(device);
+            if (SDL_JoystickIsHaptic(device))
+                sdldev->sdl_haptic = SDL_HapticOpenFromJoystick(device);
+
+            InterlockedExchangePointer((void**)&sdldev->sdl_js, device);
             continue;
         }
 
         device = SDL_JoystickOpen(i);
         sdldev->instance_id = SDL_JoystickInstanceID(device);
+        sdldev->sdl_guid = SDL_JoystickGetGUID(device);
 
         name = SDL_JoystickName(device);
         sdldev->name = HeapAlloc(GetProcessHeap(), 0, strlen(name) + 1);
@@ -762,10 +784,16 @@ static HRESULT poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface)
     struct device_state_item item;
     SDL_Joystick *js = This->sdldev->sdl_js;
 
+    SDL_JoystickUpdate();
+
     if(!SDL_JoystickGetAttached(js))
-        return DIERR_INPUTLOST;
+    {
+        find_sdldevs();
 
-    SDL_JoystickUpdate();
+        js = This->sdldev->sdl_js;
+        if(!SDL_JoystickGetAttached(js))
+            return DIERR_INPUTLOST;
+    }
 
     while(This->enum_device_state(js, This, &item, i++))
     {
@@ -1583,6 +1611,7 @@ static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
 {
     JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
+    find_sdldevs();
     return sdl_input_get_info_W(This->sdldev->sdl_js, guid, pdei);
 }
 
@@ -1592,6 +1621,7 @@ static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
 {
     JoystickImpl* This = impl_from_IDirectInputDevice8A(iface);
     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
+    find_sdldevs();
     return sdl_input_get_info_A(This->sdldev->sdl_js, guid, pdei);
 }
 
From ff58d2a0390fb7d228722408bbac050d7a586a2e Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 1 Oct 2019 12:26:39 -0500
Subject: [PATCH] xinput: Recheck when a device notification is sent

Dark Souls Remasters checks for xinput devices when it receives a
WM_DEVICECHANGE message. We would only poll for new devices if it had
been at least 2 seconds since the last check. So often, DS would receive
the message, but we would refuse to poll for devices, so the game would
think no controller was present.

This commit fixes that by also subscribing to event notifications and
triggering a poll. This isn't quite what Windows does. If the game
subscribes to event notifications _before_ our xinput does, then it
could still fail as Dark Souls does (but Dark Souls does not register in
that order). For this reason we also keep the timeout mechanism, which
will hopefully work for any games that are unlucky enough to fall into
this corner case.
---
 dlls/xinput1_1/Makefile.in   |   2 +-
 dlls/xinput1_2/Makefile.in   |   2 +-
 dlls/xinput1_3/Makefile.in   |   2 +-
 dlls/xinput1_3/hid.c         |  16 -----
 dlls/xinput1_3/xinput_main.c | 127 +++++++++++++++++++++++++++++++++--
 dlls/xinput1_4/Makefile.in   |   2 +-
 dlls/xinput9_1_0/Makefile.in |   2 +-
 7 files changed, 128 insertions(+), 25 deletions(-)

diff --git a/dlls/xinput1_1/Makefile.in b/dlls/xinput1_1/Makefile.in
index d1c425872e1..743ad448ecb 100644
--- a/dlls/xinput1_1/Makefile.in
+++ b/dlls/xinput1_1/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = xinput1_1.dll
 PARENTSRC = ../xinput1_3
-DELAYIMPORTS = hid setupapi
+DELAYIMPORTS = hid setupapi user32
 
 C_SRCS = \
 	hid.c \
diff --git a/dlls/xinput1_2/Makefile.in b/dlls/xinput1_2/Makefile.in
index 822ec157ac8..ed00fed1ab7 100644
--- a/dlls/xinput1_2/Makefile.in
+++ b/dlls/xinput1_2/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = xinput1_2.dll
 PARENTSRC = ../xinput1_3
-DELAYIMPORTS = hid setupapi
+DELAYIMPORTS = hid setupapi user32
 
 C_SRCS = \
 	hid.c \
diff --git a/dlls/xinput1_3/Makefile.in b/dlls/xinput1_3/Makefile.in
index 9c873f93889..907a56cc13f 100644
--- a/dlls/xinput1_3/Makefile.in
+++ b/dlls/xinput1_3/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = xinput1_3.dll
 IMPORTLIB = xinput
-DELAYIMPORTS = hid setupapi
+DELAYIMPORTS = hid setupapi user32
 
 C_SRCS = \
 	hid.c \
diff --git a/dlls/xinput1_3/hid.c b/dlls/xinput1_3/hid.c
index 49099605e44..9d43f0edbda 100644
--- a/dlls/xinput1_3/hid.c
+++ b/dlls/xinput1_3/hid.c
@@ -70,8 +70,6 @@ struct hid_platform_private {
     struct axis_info lx, ly, triggers, rx, ry;
 };
 
-static DWORD last_check = 0;
-
 static void MarkUsage(struct hid_platform_private *private, WORD usage, LONG min, LONG max, USHORT bits)
 {
     struct axis_info info = {min, max-min, bits};
@@ -217,19 +215,6 @@ void HID_find_gamepads(xinput_controller *devices)
     DWORD idx;
     int i, open_device_idx;
 
-    idx = GetTickCount();
-    if ((idx - last_check) < 2000)
-        return;
-
-    EnterCriticalSection(&xinput_crit);
-
-    if ((idx - last_check) < 2000)
-    {
-        LeaveCriticalSection(&xinput_crit);
-        return;
-    }
-    last_check = idx;
-
     HidD_GetHidGuid(&hid_guid);
     hid_guid.Data4[7]++; /* HACK: look up the xinput-specific devices */
 
@@ -296,7 +281,6 @@ void HID_find_gamepads(xinput_controller *devices)
     }
     HeapFree(GetProcessHeap(), 0, data);
     SetupDiDestroyDeviceInfoList(device_info_set);
-    LeaveCriticalSection(&xinput_crit);
 }
 
 static void remove_gamepad(xinput_controller *device)
diff --git a/dlls/xinput1_3/xinput_main.c b/dlls/xinput1_3/xinput_main.c
index 1c6895d4403..bfef381d086 100644
--- a/dlls/xinput1_3/xinput_main.c
+++ b/dlls/xinput1_3/xinput_main.c
@@ -26,6 +26,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
+#include "winuser.h"
 
 #include "xinput.h"
 #include "xinput_private.h"
@@ -35,6 +36,8 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(xinput);
 
+static HINSTANCE xinput_instance;
+
 /* xinput_crit guards controllers array */
 static CRITICAL_SECTION_DEBUG xinput_critsect_debug =
 {
@@ -96,11 +99,79 @@ static void unlock_device(xinput_controller *device)
     LeaveCriticalSection(&device->crit);
 }
 
+static BOOL should_check = TRUE;
+static BOOL msg_wnd_quit;
+static HWND msg_wnd;
+static HANDLE msg_wnd_thread;
+static HINSTANCE msg_wnd_module;
+
+static LRESULT CALLBACK device_notification_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch(msg)
+    {
+    case WM_DEVICECHANGE:
+        should_check = TRUE;
+        break;
+    case WM_USER:
+        DestroyWindow(msg_wnd);
+        break;
+    case WM_DESTROY:
+        msg_wnd_quit = TRUE;
+        break;
+    }
+    return DefWindowProcW(hwnd, msg, wparam, lparam);
+}
+
+static DWORD WINAPI msg_wnd_threadproc(void *user)
+{
+    MSG msg;
+    WNDCLASSEXW cls = {0};
+    HANDLE inst;
+    HANDLE evt = user;
+
+    static const WCHAR xinput_class[] = {'_','_','w','i','n','e','_','x','i','n','p','u','t','_','d','e','v','n','o','t','i','f','y',0};
+
+    cls.cbSize = sizeof(cls);
+    cls.hInstance = xinput_instance;
+    cls.lpszClassName = xinput_class;
+    cls.lpfnWndProc = device_notification_wndproc;
+    RegisterClassExW(&cls);
+
+    msg_wnd = CreateWindowExW(0, xinput_class, NULL, 0, 0, 0, 0, 0,
+            HWND_MESSAGE, NULL, NULL, NULL);
+
+    RegisterDeviceNotificationW(msg_wnd, NULL, 0);
+
+    msg_wnd_quit = FALSE;
+
+    SetEvent(evt);
+    evt = NULL;
+
+    while(!msg_wnd_quit && ((int)GetMessageW(&msg, msg_wnd, 0, 0)) > 0)
+    {
+        TranslateMessage(&msg);
+        DispatchMessageW(&msg);
+    }
+
+    EnterCriticalSection(&xinput_crit);
+    inst = msg_wnd_module;
+    msg_wnd_module = NULL;
+    CloseHandle(msg_wnd_thread);
+    msg_wnd_thread = NULL;
+    msg_wnd = NULL;
+    LeaveCriticalSection(&xinput_crit);
+
+    FreeLibraryAndExitThread(inst, 0);
+
+    return 0;
+}
+
 BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
 {
     switch(reason)
     {
         case DLL_PROCESS_ATTACH:
+            xinput_instance = inst;
             DisableThreadLibraryCalls(inst);
             break;
         case DLL_PROCESS_DETACH:
@@ -111,6 +182,43 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
     return TRUE;
 }
 
+static void find_gamepads(void)
+{
+    static ULONGLONG last_check = 0;
+    ULONGLONG now;
+
+#define DELAY_BETWEEN_CHECKS_MS 2000
+
+    now = GetTickCount64();
+    if (!should_check && (now - last_check) < DELAY_BETWEEN_CHECKS_MS)
+        return;
+
+    EnterCriticalSection(&xinput_crit);
+
+    if (!msg_wnd_thread)
+    {
+        HANDLE evt = CreateEventW(NULL, 0, 0, NULL);
+        GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                (const WCHAR *)&msg_wnd_threadproc, &msg_wnd_module);
+        msg_wnd_thread = CreateThread(NULL, 0, &msg_wnd_threadproc, evt, 0, NULL);
+        WaitForSingleObject(evt, INFINITE);
+        CloseHandle(evt);
+    }
+
+    if (!should_check && (now - last_check) < DELAY_BETWEEN_CHECKS_MS)
+    {
+        LeaveCriticalSection(&xinput_crit);
+        return;
+    }
+
+    last_check = now;
+    should_check = FALSE;
+
+    HID_find_gamepads(controllers);
+
+    LeaveCriticalSection(&xinput_crit);
+}
+
 void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
 {
     int index;
@@ -121,7 +229,18 @@ void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
     to the controllers. Setting to true will send the last vibration
     value (sent to XInputSetState) to the controller and allow messages to
     be sent */
-    HID_find_gamepads(controllers);
+
+    if (enable)
+    {
+        find_gamepads();
+    }
+    else
+    {
+        EnterCriticalSection(&xinput_crit);
+        if(msg_wnd)
+            PostMessageW(msg_wnd, WM_USER, 0, 0);
+        LeaveCriticalSection(&xinput_crit);
+    }
 
     for (index = 0; index < XUSER_MAX_COUNT; index ++)
     {
@@ -137,7 +256,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION* vib
 
     TRACE("(index %u, vibration %p)\n", index, vibration);
 
-    HID_find_gamepads(controllers);
+    find_gamepads();
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
@@ -158,7 +277,7 @@ static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state)
     if (!state)
         return ERROR_BAD_ARGUMENTS;
 
-    HID_find_gamepads(controllers);
+    find_gamepads();
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
@@ -222,7 +341,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, X
 {
     TRACE("(index %u, flags 0x%x, capabilities %p)\n", index, flags, capabilities);
 
-    HID_find_gamepads(controllers);
+    find_gamepads();
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
diff --git a/dlls/xinput1_4/Makefile.in b/dlls/xinput1_4/Makefile.in
index 7acb9dc061b..2817636e475 100644
--- a/dlls/xinput1_4/Makefile.in
+++ b/dlls/xinput1_4/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = xinput1_4.dll
 PARENTSRC = ../xinput1_3
-DELAYIMPORTS = hid setupapi
+DELAYIMPORTS = hid setupapi user32
 
 C_SRCS = \
 	hid.c \
diff --git a/dlls/xinput9_1_0/Makefile.in b/dlls/xinput9_1_0/Makefile.in
index c07022a70ba..05806489d02 100644
--- a/dlls/xinput9_1_0/Makefile.in
+++ b/dlls/xinput9_1_0/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = xinput9_1_0.dll
 PARENTSRC = ../xinput1_3
-DELAYIMPORTS = hid setupapi
+DELAYIMPORTS = hid setupapi user32
 
 C_SRCS = \
 	hid.c \

From 41c7d676d89d3aa3a4add4bedb1ec89c8a4d89f8 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 1 Oct 2019 12:40:24 -0500
Subject: [PATCH] xinput: Check for new devices in the notification thread

---
 dlls/xinput1_3/xinput_main.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/dlls/xinput1_3/xinput_main.c b/dlls/xinput1_3/xinput_main.c
index bfef381d086..a663c021543 100644
--- a/dlls/xinput1_3/xinput_main.c
+++ b/dlls/xinput1_3/xinput_main.c
@@ -99,18 +99,19 @@ static void unlock_device(xinput_controller *device)
     LeaveCriticalSection(&device->crit);
 }
 
-static BOOL should_check = TRUE;
 static BOOL msg_wnd_quit;
 static HWND msg_wnd;
 static HANDLE msg_wnd_thread;
 static HINSTANCE msg_wnd_module;
 
+static void find_gamepads(BOOL force);
+
 static LRESULT CALLBACK device_notification_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
 {
     switch(msg)
     {
     case WM_DEVICECHANGE:
-        should_check = TRUE;
+        find_gamepads(TRUE);
         break;
     case WM_USER:
         DestroyWindow(msg_wnd);
@@ -182,7 +183,7 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
     return TRUE;
 }
 
-static void find_gamepads(void)
+static void find_gamepads(BOOL force)
 {
     static ULONGLONG last_check = 0;
     ULONGLONG now;
@@ -190,7 +191,7 @@ static void find_gamepads(void)
 #define DELAY_BETWEEN_CHECKS_MS 2000
 
     now = GetTickCount64();
-    if (!should_check && (now - last_check) < DELAY_BETWEEN_CHECKS_MS)
+    if (!force && (now - last_check) < DELAY_BETWEEN_CHECKS_MS)
         return;
 
     EnterCriticalSection(&xinput_crit);
@@ -205,14 +206,13 @@ static void find_gamepads(void)
         CloseHandle(evt);
     }
 
-    if (!should_check && (now - last_check) < DELAY_BETWEEN_CHECKS_MS)
+    if (!force && (now - last_check) < DELAY_BETWEEN_CHECKS_MS)
     {
         LeaveCriticalSection(&xinput_crit);
         return;
     }
 
     last_check = now;
-    should_check = FALSE;
 
     HID_find_gamepads(controllers);
 
@@ -232,7 +232,7 @@ void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
 
     if (enable)
     {
-        find_gamepads();
+        find_gamepads(FALSE);
     }
     else
     {
@@ -256,7 +256,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION* vib
 
     TRACE("(index %u, vibration %p)\n", index, vibration);
 
-    find_gamepads();
+    find_gamepads(FALSE);
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
@@ -277,7 +277,7 @@ static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state)
     if (!state)
         return ERROR_BAD_ARGUMENTS;
 
-    find_gamepads();
+    find_gamepads(FALSE);
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
@@ -341,7 +341,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, X
 {
     TRACE("(index %u, flags 0x%x, capabilities %p)\n", index, flags, capabilities);
 
-    find_gamepads();
+    find_gamepads(FALSE);
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
From 7f0040cd9bc1f830140053422294af8f6ecb2305 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 18 Nov 2019 14:30:03 -0600
Subject: [PATCH] dinput: Assign FFACTUATOR bit to the correct axes

Squash onto "dinput: Fix DS4 object enumeration order".
---
 dlls/dinput/joystick_sdl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c
index 324e8679601..78e6f08391a 100644
--- a/dlls/dinput/joystick_sdl.c
+++ b/dlls/dinput/joystick_sdl.c
@@ -1000,7 +1000,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
             case ITEM_TYPE_AXIS:
                 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[item.id], df->dwObjSize);
                 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(item.id) | DIDFT_ABSAXIS;
-                if (newDevice->sdldev->sdl_haptic && i < 2)
+                if (newDevice->sdldev->sdl_haptic && item.id < 2)
                      df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
 
                 newDevice->generic.props[idx].lDevMin = -32768;
